2009-06-15 8 views
2

Lassen Sie uns sagen, ich habe:Wie kann ich auf eine statische Eigenschaft in einer Unterklasse zugreifen, wenn sich die Eigenschaft in einer Basisklasse befindet?

public class Fruit 
{ 

    public static List<String> Suppliers { get; protected set; } 

    static Fruit() 
    { 
     Suppliers = new List<String>(); 
     Suppliers.Add("Company A"); 
    } 

} 

public class Banana : Fruit 
{ 

    static Banana() 
    { 
     Suppliers.Add("Company B"); 
    } 

} 

Wenn ich dies tun, nur im Telefonvorwahl:

foreach(String supplier in Banana.Suppliers) 
    Console.WriteLine(supplier); 

ich:

  • Firma A

Während, wenn Ich mache:

Banana b = new Banana(); 
foreach(String supplier in Banana.Suppliers) 
    Console.WriteLine(supplier); 

I (das gewünschte Ergebnis) erhalten:

  • Firma A
  • Firma B

Edit: Nachdem die Antworten zu lesen Ich verstehe, dass dies nicht funktionieren wird .

Was ich in meinem Produktionscode möchte, ist eine Liste von Werten, die dem Objekttyp gemein ist, und ich möchte dynamisch zu dieser Liste von Zeichenfolgen basierend auf dem Untertyp verschiedene Werte hinzufügen. (Der Kontext ist LDAP - alle Einträge haben objectClass = top und alle Benutzerobjekte haben objectClass = user, top, organizationPerson, person). Vermute ich muss eine Schnittstelle oder verschiedene Listen in jeder Unterklasse oder etwas verwenden, wenn niemand einen besseren Vorschlag hat?

+0

Sie sollten eine neue Frage für Ihre Bearbeitung stellen. – JoshBerke

+0

@Josh - Ich denke, der Kontext ist immer noch derselbe, ich dachte nur, dass ich glaube, dass es einen triftigen Grund gibt zu versuchen, was ich versuche ... – antirysm

+0

Ich glaube nicht, dass @Josh die Gültigkeit Ihrer Frage in Frage stellt nur, dass Sie wahrscheinlich bessere Antworten erhalten, wenn Sie die Bearbeitung auf eine eigene Frage mit einem passenden Titel und ohne das Gepäck des ursprünglichen Kontextes aufteilen. –

Antwort

13

Zum einen ist der Zugriff auf Banana.Suppliers irreführend. Es wird immer das gleiche Ergebnis wie Zugriff auf Apple.Suppliers etc - Sie haben eine einzelne Sammlung von Lieferanten.

Grundsätzlich Zugriff auf Banana.Suppliers der Compiler sendet einen Aufruf an Fruit.Suppliers: Deshalb löst nur Aufruf Banana.Suppliers nicht den statischen Konstruktor aus, der den Bananenlieferanten hinzufügt.

Der Grund, warum Sie nur die Lieferanten sehen, die im statischen Konstruktor für Bananen hinzugefügt wurden, nachdem Sie eine Banane erstellt haben, bewirkt, dass der statische Konstruktor ausgeführt wird. Sie könnten alles andere tun, was den statischen Initialisierer zum Ausführen zwang, und Sie würden die gleichen Ergebnisse erhalten. Ein Beispiel wäre das Aufrufen einer statischen Methode innerhalb von Banana selbst.

Jetzt vermute ich stark, dass Sie ein signifikantes Problem haben, dass Sie die gleichen Lieferanten für alle Arten verwenden werden. Offensichtlich ist dies nicht Ihr echter Code, und die beste Lösung hängt davon ab, was Sie Ihren wirklichen Code tun möchten. Generics können Sie effektiv "pro Typ" statische Variablen mit Typargumenten geben: Foo<Banana>.StaticProperty und Foo<Apple>.StaticProperty wird wirklich anders sein, unter der Annahme StaticProperty wird in Foo<T> deklariert.

EDIT: In Bezug auf Ihre Bearbeitung, würde ich vorschlagen, die Verwendung von Statik hier zu vermeiden.Möglicherweise erstellen Sie für jeden Typ eine Factory (implementieren eine Schnittstelle, die generisch sein kann). Beachten Sie, dass Sie möglicherweise vermeiden können, für jeden Untertyp eine separate Factory Typ zu erstellen, wenn Sie eine geeignete Instanz mit allen relevanten Elementen für jeden Typ erstellen können.

Wir müssten wirklich mehr von einem Beispiel zu sagen, um sicher zu sagen, aber im Allgemeinen finde ich, dass je weniger statische Daten Sie haben, desto testbarer ist Ihr Design und desto weniger werden Sie Probleme wie dies :)

+0

Downwoters: Bitte hinterlassen Sie einen Kommentar ... –

+0

Bitte beachten Sie meine Bearbeitung für eine Erklärung der Produktionscode Umgebung. Macht mehr Sinn...Irgendwelche Gedanken, wie man das umsetzt? – antirysm

+0

Es war ich Jon, ich habe es falsch gelesen oder vielleicht bearbeitet, aber zuerst dachte ich nicht, dass du erwähnt hast, dass es damit zu tun hat, dass er den statischen Konstruktor von Bannana nicht auslöst. Anyways jetzt ist es klar, so dass ich +1 – JoshBerke

6

Die Ergebnisse, die Sie sehen, werden durch die Art verursacht, wie statische Konstruktoren arbeiten. Die CLR führt den statischen Konstruktor nicht automatisch aus, da die erste Instanz verwendet wird, weshalb Sie in Ihrem zweiten Beispiel nur die gewünschten Ergebnisse erhalten. Weitere Informationen finden Sie unter MSDN.

2

Das cuase von diesem ist ziemlich leicht erklärbar, tatsächlich. Wenn Sie Banana.Suppliers erhalten, verweisen Sie eigentlich nur auf Fruit.Suppliers - der Compiler löst in diesem Fall die Klasse Fruit auf, weil die Vererbung funktioniert (in der Klasse Banana ist nichts definiert. Daher wird Ihr statischer Konstruktor für Banana nicht aufgerufen im ersten Beispiel, weil Sie die Klasse technisch noch nicht referenziert haben. Das ist natürlich der Grund, warum Sie den Artikel "Firma B" im ersten Ergebnis vermissen.

Das Problem hier ist ein Design-Problem. m nicht sicher, was genau Ihre Absichten hier sind, aber wenn Sie tatsächlich eine Eigenschaft in der Fruit Klasse möchten, um die Liste alle Lieferanten zu speichern, dann müssen Sie die Liste vollständig innerhalb der statischen Nachteile initialisieren Truss für die Fruit Klasse. Im Allgemeinen würde ich jedoch denken, dass Sie für diesen Zweck eine separate Dataset-Klasse oder dergleichen möchten. Statische Eigenschaften sind wahrscheinlich nicht der Weg, um dieses Designmerkmal zu erreichen.

1

Banana.Suppliers Zugriff wird auf einen Zugriff von Fruit.Suppliers zusammengestellt ... was bedeutet, dass effektiv Ihr Code nicht tatsächlich die Banana Klasse zu berühren, was bedeutet, dass .NET keinen Grund hat, den statischen Konstruktor von Banana auszuführen.

Wenn Sie mit der Klasse Banana so ziemlich alles andere gemacht haben (wie zum Beispiel das Erstellen einer Instanz), wird der statische Konstruktor Banana ausgeführt.

0

Ein statischer Konstruktor verhält sich nicht wie ein Instanzkonstruktor (sie werden nicht explizit aufgerufen). Sie müssen auf eine Eigenschaft zugreifen, die sich tatsächlich in der Banana-Klasse befindet, bevor sie erstellt wird. Sie versuchen, einige Prinzipien der Objektorientierung auf das statische Verhalten von Klassen anzuwenden. Sie sind nicht gleichberechtigt, und dies zu tun, führt dich auf eine Route, die dich letztendlich zur Verzweiflung führen wird.

Dieser Code:

foreach(String supplier in Banana.Suppliers) 
    Console.WriteLine(supplier); 

ist das genaue Äquivalent zu diesem Code:

foreach(String supplier in Fruit.Suppliers) 
    Console.WriteLine(supplier); 

So ist der statische Konstruktor auf Banana wird nie genannt, weil es nie gebraucht wird. Der folgende Code zeigt, wie der Aufruf eines statischen Members von fruit dazu führt, dass der statische Konstruktor aufgerufen wird und die gewünschten Ergebnisse liefert.

public class Banana : Fruit 
{ 
    static Banana() 
    { 
     Suppliers.Add("Company B"); 
    } 
    public static void Foo() 
    { 

    } 
} 

// ... 
Banana.Foo(); 
foreach (var supplier in Banana.Suppliers) 
    Console.WriteLine(supplier); 
+0

Eigentlich hat Jon Skeet bereits darauf hingewiesen, dass dies nicht so funktioniert, wie ich es beabsichtigt habe, da es nur eine statische Liste von Früchten gibt. Schätze, ich bin nicht der Einzige, der ein bisschen verwirrt ist ... ;-) – antirysm

+0

Ja, ich habe zu lange gebraucht, um meine Antwort zu verfassen. Ich habe es jedoch verlassen, weil das Beispiel unten eine andere Möglichkeit zeigt, den statischen Konstruktor zum Aufruf zu zwingen. –

Verwandte Themen