2009-11-24 13 views
26

Ich habe eine Reihe von Schnittstellen, die in enger Verbindung mit bestimmten veränderbaren Objekt verwendet werden.C#: Schnittstelle Vererbung Getter/Setter

Viele Benutzer des Objekts benötigen nur die Fähigkeit, Werte aus dem Objekt und dann nur einige wenige Eigenschaften zu lesen. Um Namespace-Verschmutzungen zu vermeiden (leichtere Intellisense) und um die Verwendungsabsicht zu überwinden, hätte ich gerne eine kleine Basisschnittstelle, die nur einige "Schlüssel" -Eigenschaften schreibgeschützt verfügbar macht.

Fast alle Implementierungen unterstützen jedoch die vollständige Schnittstelle, einschließlich Modifizierbarkeit.

Leider lief ich in eine Straßensperre dieses Konzept in C# zum Ausdruck:

interface IBasicProps { 
    public int Priority { get; } 
    public string Name {get;} 
    //... whatever 
} 

interface IBasicPropsWriteable:IBasicProps { 
    public int Priority { set; } //warning CS0108: [...] hides inherited member [...] 
    public string Name { set; } 
    //... whatever 
} 

ich sicherlich keine Mitglieder zu verbergen beabsichtigte, so dass aint gut!

Natürlich kann ich das mit Methoden gut lösen, aber was ist die richtige Wahl? Ich möchte die "Kern" -Schnittstelle so klein wie möglich halten, selbst wenn das Aufteilen der Schnittstellen keinen anderen Zweck als die Absicht der Kommunikation erfüllt. Mit geteilten Schnittstellen ist es nur sehr offensichtlich, welche Methoden keine Aktualisierung durchführen werden, und es macht das Schreiben von Code ein wenig klarer (nicht zu vergessen, erlaubt auch nice-n-einfache statische Singleton-Stubs, die für einige einfache Fälle ausreichen) .

Ich möchte abstrakte Klassen und dergleichen vermeiden; Sie machen Reimplementierungen oder schnelle Einzweck-Shims, die komplexer und schwer zu ranken sind.

Also, Ideen?

Antwort

27

Methode, die sich in einer Schnittstelle versteckt, ist nicht annähernd so grungy; Ich würde gehen mit so etwas wie:

interface IBasicProps { 
    int Priority { get; } 
    string Name {get;} 
    //... whatever 
} 

interface IBasicPropsWriteable:IBasicProps { 
    new int Priority { get; set; } 
    new string Name { get; set; } 
    //... whatever 
} 
class Foo : IBasicPropsWriteable { 
    public int Priority {get;set;} 
    public string Name {get;set;} 
/* optional 
    int IBasicProps.Priority {get {return Priority;}} 
    string IBasicProps.Name {get {return Name;}} 
*/ 
} 
+0

Als Bonus funktioniert das auch gut mit Co/Kontravarianz; Mein tatsächlicher Anwendungsfall ist mehr wie 'IBasicProps {/*...*/} IBasicPropsWriteable : IBasicProps {}'. Hier ist die Schnittstellenaufteilung notwendig; Ein zentraler Manager möchte, dass die Kovarianz verschiedene Objekte in einer Sammlung ablegt, aber die einzelnen Besitzer jedes Objekts wissen, welchen Typ es enthält. –

+1

Was denken Sie über das Auslassen des Getters auf der neuen Eigenschaft in der abgeleiteten Schnittstelle, wie hier vorgeschlagen? http: // Stapelüberlauf.com/questions/3516279/interface-vererbung-is-extending-properties-possible # comment3676924_3516450 –

+0

Während dies wahrscheinlich die schlechteste Lösung in C# ist, ist es immer noch unbefriedigend, den "neuen" Modifikator zu verwenden, weil es bedeutet "es gibt ein nicht verwandte Eigenschaft desselben Namens, die alte Eigenschaft ist noch erhalten, benötigt jedoch eine komplexere Syntax für den Zugriff über 'IBasicPropsWriteable' ". Es wäre schön, wenn Sie die "selbe" Eigenschaft mit einem zusätzlichen Accessor erweitern könnten. (Das gleiche Problem tritt auf, wenn Klassen sich gegenseitig erben.) Mit Ihrer Lösung wird der Code 'static void M (IBasicPropsWriteable x) {var a = x.Name; var b = ((IBasicProps) x) .Name; } 'liest formell zwei verschiedene Mitglieder! –

4

Wenn Ihr Ziel ist es klarer zu machen ist, wenn vs. Schreiben Lesen erlaubt ist, dann würde ich getrennte Getter und Setter-Methoden verwenden, anstatt Eigenschaften.

interface IBasicProps { 
    int GetPriority(); 
    string GetName(); 
    //... whatever 
} 

interface IBasicPropsWriteable:IBasicProps { 
    void SetPriority(int priority); 
    void SetName(string name); 
    //... whatever 
} 
+3

Dies ist kein Schönheitswettbewerb Gewinner, aber es sieht aus wie es ist eine ziemlich sichere Wette, es wird gut funktionieren ... –

2

Ein Weg könnte einfach sein, die Vererbung der Schnittstellen zu überspringen. Machen Sie eine Nur-Lese-Schnittstelle und ein Nur-Schreib-und implementieren als notwendig:

interface IBasicPropsReadable { 
    int Priority { get; } 
    string Name { get; } 
} 

interface IBasicPropsWriteable { 
    int Priority { set; } 
    string Name { set; } 
} 

class SomeClassReadWrite : IBasicPropsReadable, IBasicPropsWriteable { 
    int Priority { get; set; } 
    string Name { get; set; } 
} 

class SomeClassReadOnly : IBasicPropsReadable { 
    int Priority { get; } 
    string Name { get; } 
} 
+2

Dies lässt mich nicht den gängigen Begriff "ein Lese-/Schreib-Objekt" ausdrücken. Wie kann ich jetzt eine Methode schreiben, die nur Lese-/Schreibvarianten akzeptiert? –

1

Sie die Schnittstellen nicht verwandten verlassen und einfach haben Ihre Klasse beide Schnittstellen implementieren. Schließlich definieren alle Schnittstellen den Vertrag und die Verträge müssen nicht miteinander verknüpft werden. Es scheint, dass es nur eine Optimierung für Sie ist, wenn das Schreiben von der anderen abgeleitet wird, so dass Sie nur eine Schnittstelle angeben müssen.

public interface IBasicProps 
{ 
    int Priority { get; } 
    string Name {get;} 
    //... whatever 
} 

public interface IBasicPropsWriteable 
{ 
    int Priority { get; set; } 
    string Name { get; set; } 
    //... whatever 
} 

public class Foo : IBasicProps, IBasicPropsWriteable 
{ 
    public int Priority { get; set; } 
    public string Name { get; set; } 

    // whatever 
} 

Wenn Sie wirklich die Optimierung brauchten, könnten Sie eine andere Schnittstelle erstellen, die von beiden abgeleitet ist und von Ihren Klassen implementiert wird.

public interface IBasicPropsAll : IBasicProps, IBasicPropsWriteable { } 

public class Foo : IBasicPropsAll 
{ 
    public int Priority { get; set; } 
    public string Name { get; set; } 

    // whatever 
} 
+1

Der IBasicPropsAll-Ansatz funktioniert nicht (sehr gut). '((IBasicPropsAll) new Foo()). Name' ist mehrdeutig, und der Compiler akzeptiert es nicht. Dies bedeutet, dass Sie häufig casten müssen. –

+0

Ja. Sie würden dies nur verwenden, um die "Optimierung" zu erhalten, nicht beide Schnittstellen eingeben zu müssen. Ich empfehle es nicht. Sie würden es nicht verwenden, Sie würden in der Praxis entweder auf IBasicProps oder IBasicPropsWriteable anwenden. – tvanfosson