Wann sollte oder sollte ich generische Typ Einschränkungen verwenden nicht?
-
10-07-2019 - |
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.
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
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