2009-01-19 7 views
55

Ich war auf der Suche auf C# Auflistungsinitialisierer und fand die Umsetzung sehr pragmatisch sein, aber auch sehr anders als alles andere in C#Warum funktionieren C# -Auflistungsinitialisierer so?

ich in der Lage bin Code wie folgt zu erstellen:

using System; 
using System.Collections; 

class Program 
{ 
    static void Main() 
    { 
     Test test = new Test { 1, 2, 3 }; 
    } 
} 

class Test : IEnumerable 
{ 
    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public void Add(int i) { } 
} 

Da ich die zufrieden Mindestanforderungen für den Compiler (implementiert IEnumerable und public void Add) funktioniert das aber hat offensichtlich keinen Wert.

Ich fragte mich, was verhinderte, dass das C# -Team strengere Anforderungen stellte? Mit anderen Worten, warum benötigt der Compiler für die Kompilierung dieser Syntax nicht, dass der Typ ICollection implementiert? Das scheint eher im Geiste anderer C# -Features zu liegen.

+3

Nur eine Anmerkung für Webentwickler: NotImplementedException wird ausgelöst, wenn Sie versuchen, eine Instanz dieser Klasse zu JSON zu serialisieren (die Implementierung von IEnumerable führt dazu, dass der Serialisierer das Objekt iteriert) – diachedelic

Antwort

87

Ihre Beobachtung ist genau richtig - in der Tat spiegelt es eine von Mads Torgersen, einem Microsoft C# Language PM.

Mads einen Beitrag im Oktober 2006 zu diesem Thema What Is a Collection? in dem Titel schrieb er:

Zugegeben, wir es in der ersten Version des Frameworks mit System.Collections.ICollection blies, welches ist neben nutzlos. Aber wir es fest ziemlich gut, wenn Generika entlang in .NET Framework 2.0 kam: System.Collections.Generic.ICollection < T> können Sie Elemente hinzufügen und entfernen, sie aufzuzählen, Zähle sie und überprüfen für die Mitgliedschaft.

Offensichtlich von da an jeder würde implementieren ICollection <T> jedes Mal sie eine Sammlung machen, nicht wahr? Nicht so. Hier erfahren Sie, wie wir LINQ verwendet haben, um zu erfahren, welche Sammlungen sind, und , wie das dazu führte, dass wir unsere Sprache Design in C# 3.0 ändern.

Es stellt sich heraus, dass es nur 14 Implementierungen von ICollection<T> im Rahmen sind, aber 189 Klassen, die IEnumerable und haben eine öffentliche Add() Methode implementieren.

Es gibt einen versteckten Vorteil für diesen Ansatz - wenn sie auf der ICollection<T> Schnittstelle basieren würde, hätte es genau eine unterstützte Add() Methode gegeben.

Im Gegensatz dazu bedeutet der Ansatz, den sie genommen haben, dass die Initialisierer für die Sammlung nur Sätze von Argumenten für die Add() Methoden bilden.

Zur Veranschaulichung, lassen Sie uns Ihren Code leicht erweitern:

class Test : IEnumerable 
{ 
    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public void Add(int i) { } 

    public void Add(int i, string s) { } 
} 

Sie jetzt dieses schreiben kann:

class Program 
{ 
    static void Main() 
    { 
     Test test 
      = new Test 
      { 
       1, 
       { 2, "two" }, 
       3 
      }; 
    } 
} 
+0

Im Zitat, zweiter Absatz 1. Zeile "ICollection " nicht "ICollection". –

+0

Sie haben Recht - ich habe es versäumt, die Buchstaben kleiner als und größer als zu kodieren. Fest. – Bevan

+0

Ihre Antwort ist ein Beispiel für die Vorteile/Flexibilität, die dies bietet, aber ich frage mich, ob dies bedeutet, dass es keine effektive Möglichkeit gibt, diese Flexibilität in ähnlichen Szenarien zu bieten. Sie haben effektiv in die Spezifikation geschrieben: "Wenn Sie zufällig eine Methode mit dem Namen' Add' irgendwo in einer Hierarchie haben, die 'IEnumerable' implementiert, dann machen wir einige Hokus-Pokus im Compiler und lassen Initialisierer es aufrufen, obwohl die Methode ist keine Überladung, kein Schnittstellenmitglied oder in irgendeiner Weise für einen solchen Zweck über Syntax, Attribute oder andere Mittel. " – AaronLS

9

ich darüber dachte auch, und die Antwort, die mich erfüllt die meisten ist, dass ICollection hat viele andere Methoden als Hinzufügen, z. B .: Löschen, Enthält, Kopieren und Entfernen.Das Entfernen von Elementen oder das Löschen hat nichts damit zu tun, dass die Objektinitialisierersyntax unterstützt werden kann, alles was Sie brauchen, ist ein Add().

Wenn das Framework granular genug entworfen wurde und es eine ICollectionAdd-Schnittstelle gab, dann hätte es ein "perfektes" Design. Aber ich glaube ehrlich gesagt nicht, dass das viel Wert hinzugefügt hätte, mit einer Methode pro Schnittstelle. IEnumerable + Add scheint ein hackischer Ansatz zu sein, aber wenn man darüber nachdenkt, ist es eine bessere Alternative.

EDIT: Dies ist nicht das einzige Mal, C# hat ein Problem mit dieser Art von Lösung angesprochen. Seit .NET 1.1 verwendet foreach die Enten-Typisierung, um eine Auflistung aufzulisten. Alle Klassen, die implementiert werden müssen, sind GetEnumerator, MoveNext und Current. Kirill Osenkov hat eine post, die auch Ihre Frage stellt.

+0

Das ist "Interface-Trennung Prinzip". –

3

(Ich weiß, ich bin 3 Jahre auf das Ende, aber ich war mit den vorhandenen Antworten nicht zufrieden.)

, warum für diese Syntax, um zu kompilieren, wird der Compiler nicht verlangen, dass die Typ implementieren ICollection?

Ich werde Ihre Frage umkehren: Was wäre es, wenn der Compiler Anforderungen hätte, die nicht wirklich benötigt werden?

Nicht-ICollection Klassen können ebenfalls von der Syntax der Sammlungsinitialisierer profitieren. Erwägen Sie Klassen, die das Hinzufügen von Daten in sie ermöglichen, ohne den Zugriff auf zuvor hinzugefügte Daten zuzulassen.

Persönlich verwende ich gerne die new Data { { ..., ... }, ... } Syntax, um den Code meiner Komponententests ein leichtes, DSL-ähnliches Aussehen hinzuzufügen.

Eigentlich würde ich eher die Anforderung schwächen, so dass ich die schön aussehende Syntax verwenden kann, ohne mich um die Implementierung von IEnumerable kümmern zu müssen. Sammlungsinitialisierer sind reiner syntaktischer Zucker zu Add(), sie sollten nichts anderes erfordern.

+4

Ich sehe Ihren Punkt, aber wenn syntaktischer Zucker eine Methode erfordert, würde ich lieber über eine Schnittstelle darüber lernen. –

+0

@ nik.shornikov So etwas wie ICollectionInitializerSyntax (und alle Varianten)? Ich stimme zu, dies würde die Absicht des Codes deutlicher machen. Und es würde gut zu meinem Wunsch passen, die aktuelle IEnumerable-Anforderung zu schwächen. –

Verwandte Themen