2009-06-04 19 views
3

Ich arbeitete in der Microsoft.Ink dll vor kurzem mit C# und debugging ein Problem (was nicht damit zusammenhängt) Ich bemerkte, dass, wenn ich es debuggte, Tinte Objekte a Hüben Objekt, das eine Tinte Objekt hatte, das hatte .... usw.Deklarieren einer Klasse als ein Mitglied von ihr

das verwirrte mich, wie ich unter der Annahme war, Sie dies nicht tun konnte (ich aus einem C++ Hintergrund kommen)

Aber ich ignorierte es, löste das Problem und ging weiter. Heute stoße ich auf ein ähnliches Problem, wenn ich mir eine Klasse anschaue, die ein privates Mitglied hat, das derselben Klasse angehört wie sie.

public sealed class Factory 
{ 

    private static Factory instance = new Factory(); 
} 

Wie ist das überhaupt möglich? Ich kann jetzt instance.instance.instance.instance ... etc aufrufen. Wie du dir vorstellen kannst, tut das meinem sterblichen Gehirn weh, und ich bin mir sicher, dass es auch am Computer nicht gut sein kann. Wie geht der Compiler damit um? Und wie tief geht das Kaninchenloch?

Antwort

7

Da es statisch ist und daher nur eine Kopie der Variablen instance in der AppDomain vorhanden ist.

Was Sie denken, dies ist:

public class Foo 
{ 
    private Foo lol = new Foo(); 
} 

Hinweis, alles ist hier beispielsweise nicht statisch.

Wie die Kommentatoren (vor langer Zeit) angemerkt haben, gilt dies syntaktisch, würde aber dazu führen, dass eine StackOverflowException ausgelöst wird, da die Zuweisung eine Konstruktion erfordert und die Konstruktion eine neue Zuweisung erstellt. Einer löst den anderen in einem Zyklus aus, der endet, wenn der Aufrufstapel seine maximale Länge erreicht.

Im OP-Beispiel erfordert die Zuweisung eine Konstruktion, aber die Zuweisung wird vom static constructor ausgelöst, nicht vom Instanzkonstruktor. Der statische Konstruktor wird nur einmal innerhalb einer Anwendungsdomäne ausgeführt, um den Typ der Klasse zu initialisieren. Es wird nicht von der Instanzkonstruktion ausgelöst und führt daher (im Beispiel von OP) nicht zu einem Stapelüberlauf.

+3

In dem Beispielbeispiel "es geht tief in den Kaninchenbau" tief genug, um eine StackOverflowException ausgelöst zu werden ... – Greg

+0

In Ihrem Beispiel würden Sie offensichtlich eine Ausnahme SO erhalten. Ob "Deklaration einer Klasse als Mitglied von sich selbst" sinnvoll ist, ist jedoch keine Frage von Statik versus Instanz. Siehe das Beispiel in meiner Antwort. –

5

es ist nicht unbedingt rekursiv von Natur aus. denke an eine verknüpfte Liste. oder ein Baum.

Es ist nur erlaubt Objekte dieser Klasse einen internen Verweis auf ein anderes Objekt dieser Klasse haben.

6

Dies ist ein Softwaremuster, das als "Singleton" bekannt ist.

Einige Leute missbilligen die Verwendung des Musters aus mehr Gründen als nur in der Frage angegeben, aber im Guten oder im Schlechten ist es ein häufiges Muster im .NET Framework. Sie finden Singleton-Eigenschaften (oder Felder) für Klassen, die nur einmal instanziiert werden sollen. Stellen Sie sich eine statische Instance-Eigenschaft als einen globalen Hook vor, an dem ein Objekt angehängt werden soll.

+0

Eigentlich ist es kein Singleton. Die Instanz ist nicht öffentlich und es gibt keinen privaten Standardkonstruktor. – Will

+1

@Will, ja, es sieht aus wie ein Teil des Singleton. Wenn es zum Beispiel eine öffentliche Eigenschaft gäbe, wäre es ein Singleton. –

+0

Meine Antwort wies darauf hin, dann sah ich, dass alle anderen auch darauf gesprungen sind. –

5

Da dies eine Klasse und keine Struktur ist, definieren Sie bei der Deklaration eines Feldes, das die Klasse darstellt, nur einen Verweis auf eine Klasse. Auf diese Weise können Sie Referenzen behalten, vorausgesetzt, Sie weisen sie zu.

In Ihrem Fall weist Ihre Referenz eine neue Klasse zu, aber sie ist statisch, also wird sie nur einmal ausgeführt, unabhängig davon, wie viele Klassen Sie erstellen. Der Instanzkonstruktor wird bei der ersten Verwendung von Factory ausgeführt und ruft einen einzelnen nicht statischen Konstruktor auf. Doing.instance.instance ist nicht erlaubt, da die Instanz statisch ist. Sie können nicht auf eine statische Variable von einem Mitglied zugreifen - Sie müssen Factory.instance ausführen.

Sie könnten ~ Instanz jedoch nicht statisch machen, und es wäre ein Verweis auf eine andere "Factory" -Klasse oder sogar eine Referenz darauf. In diesem Fall könnten Sie instance.instance.instance ketten - aber es folgen nur die Referenzen, solange Sie sie festgelegt haben. Alles funktioniert, keine Probleme.

+1

Technisch tun Factory.instance.instance.instance sollte als Fehler oder Warnung gekennzeichnet werden, da Sie auf ein statisches Mitglied zugreifen, als wäre es ein nicht-statisches Mitglied. Ich weiß in Java das erzeugt eine Warnung. – Jherico

+0

Sehr wahr. Editieren korrigieren. –

+0

+1 für die Erwähnung, dass dies nicht für Strukturen funktioniert, da diese Werttypen sind. –

1

Ich denke du solltest umgekehrt fragen: Warum sollte das nicht möglich sein? Factory ist nur ein Typ wie jeder Typ, der vom Compiler aufgelöst wird.

Da die meisten Antworten darauf hinweisen, dass dies nur funktioniert, weil Factory ein statisches Feld ist, habe ich das folgende Beispiel hinzugefügt. Bitte beachten Sie, dass dies ein sehr primitives Beispiel einer verketteten Liste ist (Sie würden es wahrscheinlich aus verschiedenen Gründen nicht so implementieren, aber ich habe noch kein besseres Beispiel gefunden). In diesem Beispiel ist ChainedListItem ein Container für ein Element einer einfach verknüpften Liste, die ein Feld des gleichen Typs enthält, um auf das nächste Element in der Liste zu zeigen. Die Liste hat ein (leeres) Kopfelement und das letzte Element wird erreicht, indem ein leeres _nextItem Feld markiert:

public class ChainedListItem<T> 
{ 
    private ChainedListItem<T> _nextItem; 
    T _content; 

    public ChainedListItem<T> NextItem 
    { 
     get { return _nextItem; } 
     set { _nextItem = value; } 
    } 

    public T Content 
    { 
     get { return _content; } 
     set { _content = value; } 
    } 

    public ChainedListItem<T> Add(T content) 
    { 
     _nextItem = new ChainedListItem<T>(); 
     _nextItem.Content = content; 
     return _nextItem; 
    } 

    public void Dump() 
    { 
     ChainedListItem<T> current = this; 
     while ((current = current.NextItem) != null) 
     { 
      Console.WriteLine(current._content); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     ChainedListItem<int> chainedList = new ChainedListItem<int>(); 
     chainedList.Add(1).Add(2).Add(3); 
     chainedList.Dump();   
    } 
} 

Der „Kaninchenbau“ geht so tief, wie Ihr Stack-Speicher Sie kann eine weiteren Anruf an den Konstruktor machen vom Typ. Wenn Sie versuchen, tiefer zu gehen, erhalten Sie eine Stapelüberlauf-Ausnahme wie bei jeder anderen Rekursion.

Übrigens zeigt der Code, den Sie in Ihrer Antwort geschrieben haben, eine sehr einfache Implementierung eines Singleton, der tatsächlich auf einem (privaten) statischen Member des gleichen Typs wie der umgebende Typ basiert.

Und, last but not least, solche Konstrukte sind auch in C++ völlig in Ordnung.

-2

Dies ist die meiste Zeit die meisten OO-Sprachen. Instanz ist ein statisches Mitglied von Factory. Dieser Code ist nicht ungewöhnlich. Es ist Standardfabrikmuster. Haben Sie auch ein Problem mit Code wie diesem?

x = x + 1; 
+1

unhöflich und ziemlich nicht hilfreich. – GuiSim

+0

Und "es ist Standard für das Fabrikmuster" ist auch falsch. Sie sollten Ihre Antwort überarbeiten oder löschen. -1 –

+0

Unhöflich? Kurz vielleicht. Aber ich war nicht unhöflich. Die Frage soll das OP dazu bringen, andere Stellen im Computing zu berücksichtigen, bei denen "Logik" nicht immer gilt. – jmucchiello

2

Es wird immer nur eine Instanz von ‚instance‘ sein, weil es statisch ist. Sie sollten nur auf Factory.instance zugreifen können.

string text = Factory.instance.ToString(); // legal 
string text2 = Factory.instance.instance.ToString(); // compiler error 
0

Es ist ein singleton. Das heißt, es gibt wirklich nur eine Instanz der Klasse.

Ist das die ganze Klasse? Typischerweise in C# finden Sie eine Singleton wie

public class SomeClass 
{ 

    static readonly SomeClass instance = new SomeClass(); 


     public static SomeClass Instance 
     { 
      get { return instance; } 
     } 


     static SomeClass() 
     { 
     } 

     SomeClass() 
     { 
     } 
} 
0

sehe ich nicht sicher bin, wie Sie sich selbst die Instanz zugreifen, da es privat ist. Das einzige, was für eine Singleton-Implementierung nützlich wäre, ist die öffentliche Eigenschaft, die die Instanz aussetzt.

Verwandte Themen