Frage

Ich habe eine Basisklasse bekommt:

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

Und zwei abgeleiteten Klassen

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!");
    }
}

Okay, jetzt sagen, ich eine Liste von Elementen haben:

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

und ich möchte, dass sie alle ihre DoSomething () -Methode aufrufen. Ich konnte erwarten, dass nur die Liste durchlaufen und rufen ihre DoSomething () -Methode, also lassen Sie uns sagen, dass ich eine Methode haben, das zu tun AllDoSomething () aufgerufen, die gerade iteriert über die Liste und macht den Job:

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

Was ist der praktische Unterschied der folgenden Methode?

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

Beiden Methoden scheinen real, obwohl syntaktisch anders zu sein, dasselbe zu tun.

Sind sie nur verschiedene Möglichkeiten, das gleiche tun? Ich verstehe, Generika und Typen Zwänge aber nicht, warum ich einen Weg, über die andere in diesem Fall verwenden würde.

War es hilfreich?

Lösung

Dies liegt daran, als der noch, ist C # nicht unterstützt Kovarianzstrukturen .

  

Formal in v2.0 C #, wenn T ein   Subtyp des U, dann T [] ist ein Subtyp von   U [], aber nicht G ist ein Subtyp von G   (Wobei G jeder generischer Typ). Im   Typ-Theorie Terminologie, wir beschreiben   dieses Verhalten durch, dass C # Array sagen   Typen sind „covariant“ und Generika   Typen sind „unveränderlich“.

Referenz: http: // blogs. msdn.com/rmbyers/archive/2005/02/16/375079.aspx

Wenn Sie die folgende Methode:

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

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

Dabei gilt, als ob Sie die generische Typen Einschränkung verwenden, wird es.

Weitere Informationen über die Kovarianz und Kontra] Besuche http://www.pabich.eu/blog/archive/2008/02/12/c-generics---parameter-variance-its-constraints-and-how-it.aspx

  • http://blogs.msdn.com/rmbyers /archive/2006/06/01/613690.aspx
  • http://msdn.microsoft.com/ en-us / library / ms228359 (VS.80) aspx
  • http://www.csharp411.com/convert-between-generic- ienumerablet /
  • http://research.microsoft.com/apps/pubs /default.aspx?id=64042
  • Warum kann List = List ?
  • Andere Tipps

    Angenommen, Sie eine Liste hatte:

    List<Stuff1> l = // get from somewhere
    

    Jetzt versuchen:

    AllDoSomething(l);
    

    Mit der generischen Version, wird es erlaubt sein. Mit dem nicht-generic, wird es nicht. Das ist der wesentliche Unterschied. Eine Liste von Stuff1 ist keine Liste von StuffBase. Aber in dem allgemeinen Fall, Sie es nicht benötigen genau eine Liste von StuffBase zu sein, so ist es flexibler.

    Sie könnten umgehen, dass, indem man zuerst die Liste des Stuff1 in eine Liste von StuffBase kopieren, um es mit der nicht-generischen Version kompatibel zu machen. Aber dann nehme man eine Methode hatte:

    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;
    }
    

    Ohne Generika, können Sie eine Liste von StuffBase akzeptieren, aber Sie würden dann eine Liste von StuffBase zurückkehren. Der Anrufer hätte Abgüsse verwenden, wenn sie wissen, dass die Gegenstände wirklich von einem abgeleiteten Typ waren. So Generika können Sie den aktuellen Typ eines Arguments bewahren und kanalisieren sie durch das Verfahren zum Rückgabetyp.

    Im Beispiel Sie vorausgesetzt, es gibt keinen Unterschied, aber versuchen Sie Folgendes:

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

    Der erste Anruf funktioniert gut, aber die zweite wegen generic Kovarianz nicht kompilieren

    Lizenziert unter: CC-BY-SA mit Zuschreibung
    Nicht verbunden mit StackOverflow
    scroll top