Domanda

Ho una classe base:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

E due classi derivate

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

Bene, ora dì che ho un elenco di elementi:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

e voglio che tutti chiamino il loro metodo DoSomething (). Potrei aspettarmi di iterare la lista e chiamare il loro metodo DoSomething (), quindi diciamo che ho un metodo per fare quello chiamato AllDoSomething () che scorre solo sulla lista e fa il lavoro:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

Qual è la differenza pratica del seguente metodo?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

Entrambi i metodi appaiono in termini reali, sebbene siano sintatticamente diversi, per fare la stessa cosa.

Sono solo modi diversi di fare la stessa cosa? Comprendo i generici e i vincoli di tipo, ma non riesco a capire perché in questo caso dovrei usare un modo rispetto all'altro.

È stato utile?

Soluzione

Questo perché C # non supporta ancora Covariance .

  

Più formalmente, in C # v2.0 se T è a   sottotipo di U, quindi T [] è un sottotipo di   U [], ma G non è un sottotipo di G   (dove G è un tipo generico). Nel   terminologia di teoria dei tipi, descriviamo   questo comportamento dicendo che l'array C #   i tipi sono & # 8220; covariante & # 8221; e generico   i tipi sono & # 8220; invariante & # 8221 ;.

Riferimento: http: // blog. msdn.com/rmbyers/archive/2005/02/16/375079.aspx

Se hai il seguente metodo:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

Dove se si utilizza il vincolo di tipo generico, lo farà.

Per ulteriori informazioni su Covariance e Contravarianza], controlla La serie di post di Eric Lippert .


Altri post che vale la pena leggere:

Altri suggerimenti

Supponi di avere un elenco:

List<Stuff1> l = // get from somewhere

Ora prova:

AllDoSomething(l);

Con la versione generica, sarà consentito. Con il non generico, non lo farà. Questa è la differenza essenziale. Un elenco di Stuff1 non è un elenco di StuffBase. Ma nel caso generico, non è necessario che sia esattamente un elenco di <=>, quindi è più flessibile.

Puoi aggirare il problema copiando prima il tuo elenco di <=> in un elenco di <=>, per renderlo compatibile con la versione non generica. Ma supponiamo che tu abbia un metodo:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

Senza generici, potresti accettare un elenco di <=>, ma dovresti quindi restituire un elenco di <=>. Il chiamante avrebbe dovuto usare i cast se sapesse che gli oggetti erano davvero di tipo derivato. Quindi i generici ti consentono di preservare il tipo effettivo di un argomento e di canalizzarlo attraverso il metodo al tipo restituito.

Nell'esempio che hai fornito non c'è alcuna differenza, ma prova quanto segue:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

La prima chiamata funziona bene ma la seconda non viene compilata a causa della covarianza generica

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top