2017-08-20 2 views
1

Ich habe eine Klasse Foo, die eine Basisklasse für viele andere Klassen wie Bar und Baz ist, und ich möchte einige Berechnungen innerhalb Foo mit den statischen Mitgliedern in Bar und Baz, wie unten dargestellt:C# Zugriff Kinder statische Element in Eltern

public class Foo{ 
    public result1 { 
     get{ 
      return field1; 
     } 
    } 
} 

public class Bar : Foo{ 
    public const int field1 = 5; 
} 

public class Baz : Foo{ 
    public const int field1 = 10; 
} 

Die einzige Lösung, die ich denken kann, ist es, alle Felder in einem Behälter wickeln, fügen Sie eine Funktion eine zusätzliche Kennung für jedes Objekt, und verwenden Sie die Felder zurückkehren, wie so

Bar : Foo{ 
    public readonly int id = 0; 
    public static Wrapper wrapper; 
} 

public Wrapper GetWrapper(int id){ 
    switch(id){ 
     case 0: 
      return Bar.wrapper; 
    } 
} 

Wie Sie jedoch sehen können, muss ich eine zusätzliche Klasse und Funktion beibehalten, ein d Ich würde meinen Code lieber nicht zerlegen. Gibt es eine Alternative?

+2

Sie können eine virtuelle (oder bessere abstrakte) Funktion anstelle von statischen Mitglied hinzufügen, die den Wert in Kindklasse zurückgibt ... – Khan

+0

Ja gibt es wirklich keine Möglichkeit, um so etwas wie ein virtuelles Mitglied (Getter/Get-Methode) - die Typ System erlaubt diese statische Vererbung, die Sie suchen – mfeineis

+0

Statische und Vererbung nicht gut zusammen spielen. Wenn Sie versuchen, sie zusammen spielen zu lassen, werden Sie mit einem der beiden enden, die den anderen verprügeln. – apokryfos

Antwort

0

Ich fühle mich immer wie ein Idiot, wenn ich meine eigene Frage beantworte ... Aber habe nicht gesehen, was ich erwartet habe, also könnte ich genauso gut teilen, was ich nach einer Nacht der Unverschämtheit habe.

Der Grund, warum ich die Berechnung nicht abstrakt/virtuell machen möchte, ist, dass es viele Unterklassen gibt und die Formel für alle gleich ist. Ich weigere mich einfach, den gleichen Code 10-20 Mal wiederholt zu tippen.

Konnte die statischen Felder auch nicht statisch machen, da sie auf Klassenebene zugänglich sein sollten, und sie können groß werden, und sie sind für alle Instanzen gleich.

Die einzige Lösung, die ich mit oben kommen kann, das Codefragment minimiert, ist so etwas wie dies

public class Foo { 
    public class Wrapper { 
     Fields... 
    } 
    public Wrapper wrapper; // reference 
    public int result1 { get; } 
} 

public class Bar : Foo { 
    public static Wrapper subclassWrapper; // put in the implementation 
    public Bar() : base(){ 
     wrapper = subclassWrapper; 
    } 
} 

jede Instanz So, jetzt muss eine zusätzliche Referenz halten, aber ich brauche keine Funktion zu halten. Der Wrapper wird in der Basisklasse beibehalten, sodass er weniger fragmentiert ist.

1

bearbeiten

Was Sie fordern, das heißt einen static oder const Wert in einer Unterklasse von einer Basisklasse Zugriff auf technisch möglich, aber die Prinzipien des guten SOLID OO Designs dabei verletzen. Da Sie eine Instanz einer bestimmten Unterklasse benötigen, um den Typ der Unterklasse "überdenken" zu können, um die entsprechende field1 zu erhalten, ist es wenig sinnvoll, dieses Problem statisch anzugehen.

Stattdessen ist der allgemeine, sauberere Ansatz hier subtype polymorphicism zu verwenden, der einer Aufrufmethode in der Basisklasse oder einer Methode in einer externen Klasse insgesamt den Zugriff auf den entsprechenden Wert für 'field1' basierend auf der Unterklasse ermöglicht . Dies ermöglicht, dass die Kontrolle über den zurückgegebenen Wert in den entsprechenden Unterklassen verbleibt (d. H. Gemäß Ihren Worten wird der Code nicht "fragmentiert").

Alternative Lösung unter Verwendung von Unterklasse polymorphicism (empfohlen)

Eine Unterklasse polymorpher Ansatz (dh mit dem virtual/abstract und override keywords) ermöglicht es Ihnen, das Abrufen eines Wertes (oder Objekt), die für jeden anpassbaren einzuzukapseln Unterklasse. Hier bleibt die Abstraktion konzeptionell bei "gebe mir einen ganzzahligen Wert", und dann können die unterklassenspezifischen Implementierungen von "wie", um den Wert zurückzugeben, vom Aufrufer abstrahiert (versteckt) werden. Wenn Sie die Basiseigenschaft als abstract markieren, erzwingen Sie außerdem, dass alle Unterklassen die Eigenschaft implementieren, sodass die Anforderung, einen Wert anzugeben, nicht vergessen wird.

also würde ich einen polymorphen Ansatz wie folgt empfehlen:

public abstract class Foo 
{ 
    public abstract int Result { get; } 
} 

public class Bar : Foo 
{ 
    // This is implementation specific. Hide it. 
    private const int field1 = 5; 

    public override int Result 
    { 
     get { return field1; } 
    } 
} 

public class Baz : Foo 
{ 
    public override int Result 
    { 
     // No need for this implementation to be a constant ... 
     get { return TheResultOfAReallyComplexCalculationHere(); } 
    } 
} 

Wenn es keine anderen wieder verwendbaren konkreten Methoden auf der Basisklasse Foo sind, dann könnten Sie auch die Abstraktion als Schnittstelle modellieren, mit der gleichen Wirkung :

public interface IFoo 
{ 
    int Result { get; } 
} 

dieses Problem ohne polymorphicism Annäherung an (nicht empfohlen)

All Kompilierung-Versuch benötigt in der Regel statische Felder auf Subklassen zugreifen Code wird irgendwo wechseln (oder Karte) auf die tatsächlich Art der Unterklasse-Instanz, zB:

public class Foo 
{ 
    public int result1 
    { 
     get 
     { 
      switch(this.GetType().Name) 
      { 
       case "Bar": 
        return Bar.field1; 
       case "Baz": 
        return Baz.field1; 
       default: 
        return 0; 
      } 
     } 
    } 

    public void MethodRequiringValueFromSubclass() 
    { 
     Console.WriteLine(result1); 
    } 
} 

public class Bar : Foo 
{ 
    public const int field1 = 5; 
} 

public class Baz : Foo 
{ 
    public const int field1 = 10; 
} 

Das Problem hierbei ist, dass die Open and Closed principal ist verletzt, da jedes Mal, wenn eine neue Unterklasse hinzugefügt wird, die result1-Methode geändert werden muss, um die neue Klasse aufzunehmen.

+0

Sie können die abstrakte Eigenschaft in einer nicht abstrakten Klasse nicht verwenden. Meintest du "virtual"? – Guy

+0

Danke, habe den fehlenden abstrakten Klassenqualifikator hinzugefügt – StuartLC

0

Ich würde vorschlagen, abstrakte Funktion eher als statische Element verwenden.

public abstract class Foo{ 
    public result1 { 
     get{ 
      return get_field1(); 
     } 
    } 
    protected abstract int get_field1(); 
} 

public class Bar : Foo{ 
    public const int field1 = 5; 
    protected override int get_field1() { return field1;} 
} 

public class Baz : Foo{ 
    public const int field1 = 10; 
    protected override int get_field1() { return field1;} 
} 
+0

Und was, wenn das OP nicht will, dass 'Foo' abstrakt ist? – Guy

+1

@Guy .. Dies ist nur ein Vorschlag .. Wenn er nicht will, dass es abstrakt ist, kann er virtuell verwenden. – Khan

0

Sie entweder Konstruktorparameter auf Ihre Foo Klasse hinzuzufügen, die von Erben übergeben werden können, so müssen Sie keine zusätzlichen Klassen auch werden Sie weniger Kopplung haben

public class Foo 
{ 
    private readonly int _field1; 
    public Foo(int field1) 
    { 
     _field1 = field1; 
    } 
} 

oder Sie können es verwenden, genau aus dem Vererbertyp als statisch/const Mitglieder sind Mitglieder der Klassenart

public class Foo 
{ 
    public result1 
    { 
     get 
     { 
      return Bar.field1; 
     } 
    } 
} 

aber das gibt Ihrem Code weniger Flexibilität und mehr Kopplung.

Auch haben Sie die Möglichkeit, durch virtuelle Objekte verwendet, die Sie in schließt man Klassen implementieren und verwenden können in der Basis:

public class Foo 
{ 
    public virtual int Field { get { return 0; } } 
} 
0

Statt machen Foo abstrakt als andere Antworten vorgeschlagen, dass Sie virtual verwenden können und außer Kraft setzen result1 in jeder Kind Klasse

public class Foo 
{ 
    public virtual int result1 { get; } 
} 

public class Bar : Foo 
{ 
    public const int field1 = 5; 

    public override int result1 
    { 
     get { return field1; } 
    } 
} 

public class Baz : Foo 
{ 
    public const int field1 = 10; 

    public override int result1 
    { 
     get { return field1; } 
    } 
} 

Wenn Sie result1 Standard wollen 0 etwas anderes zurück als man kann es einen anderen Wert geben

012.351.
+0

Mit der Maßgabe, dass alle anderen Unterklassen von Foo, die 'result1' nicht überschreiben, 0 zurückgeben. – StuartLC

+0

Eigentlich muss die virtuelle Getter-Eigenschaft einen Körper haben oder einen Setter haben. Falls Sie den Setter und den Körper nicht benötigen, müssen Sie ihn als abstrakt markieren. – hazevich

+0

@StuartLC Wenn Sie es als abstrakt markieren, müssen alle untergeordneten Methoden "result1" implementieren, um trotzdem einen Wert zurückzugeben. Ich habe eine Option hinzugefügt, um sie für meine Antwort zu bearbeiten. – Guy