Waarom werken initialisatieprogramma’s voor C#-verzamelingen op deze manier?

Ik keek naar initialisatieprogramma’s voor C#-verzamelingen en vond de implementatie erg pragmatisch, maar ook heel anders dan al het andere in C#

Ik kan zo code maken:

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) { }
}

Aangezien ik aan de minimumvereisten voor de compiler heb voldaan (IEnumerablegeïmplementeerd en een public void Add), werkt dit, maar heeft het duidelijk geen waarde.

Ik vroeg me af wat het C#-team ervan weerhield om strengere eisen te stellen? Met andere woorden, waarom vereist de compiler, om deze syntaxis te compileren, niet dat het type ICollectionimplementeert? Dat lijkt meer in de geest van andere C#-functies.


Antwoord 1, autoriteit 100%

Uw observatie is perfect – in feite weerspiegelt het een observatie gemaakt door Mads Torgersen, een Microsoft C# Language PM.

Mads heeft in oktober 2006 een bericht over dit onderwerp geplaatst met de titel Wat is een verzameling?waarin hij schreef:

Toegegeven, we hebben het verpest in de eerste
versie van het raamwerk met
System.Collections.ICollection, die
is zo goed als nutteloos. Maar we hebben het opgelost
best goed toen generieke geneesmiddelen langskwamen
in .NET-framework 2.0:
System.Collections.Generic.ICollection<T>
laat je elementen toevoegen en verwijderen,
noem ze op, tel ze en controleer
voor lidmaatschap.

Vanaf dat moment zou iedereen dat natuurlijk doen
ICollection<T> implementeren elke keer
ze maken een verzameling, toch? Niet zo.
Hier is hoe we LINQ hebben gebruikt om te leren
over wat collecties werkelijk zijn, en
hoe dat ons onze taal deed veranderen
ontwerp in C# 3.0.

Het blijkt dat er slechts 14 implementaties van ICollection<T>in het raamwerk zijn, maar 189 klassen die IEnumerableimplementeren en een openbare Add()methode.

Er is een verborgen voordeel aan deze aanpak: als ze het hadden gebaseerd op de ICollection<T>-interface, zou er precies één ondersteunde Add()-methode zijn geweest.

De aanpak die ze gebruikten, betekende daarentegen dat de initialisatieprogramma’s voor de verzameling slechts een reeks argumenten vormen voor de Add()-methoden.

Laten we ter illustratie uw code iets uitbreiden:

class Test : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }
    public void Add(int i) { }
    public void Add(int i, string s) { }
}

Je kunt nu dit schrijven:

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

Antwoord 2, autoriteit 10%

Ik heb hier ook over nagedacht, en het antwoord dat me het meest tevreden stelt, is dat ICollection veel andere methoden heeft dan Toevoegen, zoals: Wissen, Bevat, Kopiëren Naar en Verwijderen. Het verwijderen van elementen of wissen heeft niets te maken met het kunnen ondersteunen van de objectinitialisatiesyntaxis, u hebt alleen een Add() nodig.

Als het framework gedetailleerd genoeg was ontworpen en er een ICollectionAdd-interface was, dan zou het een “perfect” ontwerp hebben gehad. Maar ik denk eerlijk gezegd niet dat dat veel waarde zou hebben toegevoegd, met één methode per interface. IEnumerable + Add lijkt een hackachtige benadering, maar als je erover nadenkt, is het een beter alternatief.

EDIT: dit is niet de enige keer dat C# een probleem heeft benaderd met dit type oplossing. Sinds .NET 1.1 gebruikt foreach duck-typing om een ​​verzameling op te sommen, het enige dat uw klas hoeft te implementeren is GetEnumerator, MoveNext en Current. Kirill Osenkov heeft een postwaarin je vraag wordt gesteld ook.


Antwoord 3, autoriteit 3%

(Ik weet dat ik hiermee 3 jaar te laat ben, maar ik was niet tevreden met de bestaande antwoorden.)

waarom, om deze syntaxis te compileren, doet de compiler dat niet?
vereisen dat het type ICollection implementeert?

Ik zal uw vraag omkeren: wat voor nut zou het hebben als de compiler vereisten had die niet echt nodig zijn?

Niet-ICollection-klassen kunnen ook profiteren van de syntaxis van de verzamelingsinitialisatie. Overweeg klassen waarin gegevens kunnen worden toegevoegd, zonder toegang tot eerder toegevoegde gegevens.

Persoonlijk gebruik ik graag de new Data { { ..., ... }, ... }syntaxis om een ​​lichte, DSL-achtige look aan de code van mijn apparaat toe te voegen testen.

Eigenlijk zou ik liever de vereiste afzwakken, zodat ik de mooi ogende syntaxis kan gebruiken zonder zelfs maar de moeite te nemen om IEnumerablete implementeren. Verzamelinitialisatoren zijn pure syntactische suikers om toe te voegen(), ze zouden niets anders nodig hebben.

Other episodes