Reifizierung bedeutet im Allgemeinen (außerhalb der Informatik) "etwas Reales machen".
Bei der Programmierung wird etwas geändert, wenn wir in der Sprache selbst auf Informationen darüber zugreifen können.
Nehmen wir für zwei vollständig nicht generikabezogene Beispiele für etwas, das C # tut und nicht geändert hat, Methoden und Speicherzugriff.
OO-Sprachen haben im Allgemeinen Methoden (und viele, die keine ähnlichen Funktionen haben , jedoch nicht an eine Klasse gebunden sind). Als solches können Sie eine Methode in einer solchen Sprache definieren, aufrufen, möglicherweise überschreiben und so weiter. Nicht in all diesen Sprachen können Sie die Methode selbst als Daten für ein Programm behandeln. Mit C # (und wirklich mit .NET anstelle von C #) können Sie MethodInfo
Objekte verwenden, die die Methoden darstellen, sodass in C # Methoden angepasst werden. Methoden in C # sind "erstklassige Objekte".
Alle praktischen Sprachen haben einige Mittel, um auf den Speicher eines Computers zuzugreifen. In einer einfachen Sprache wie C können wir uns direkt mit der Zuordnung zwischen numerischen Adressen befassen, die vom Computer verwendet werden. Dies int* ptr = (int*) 0xA000000; *ptr = 42;
ist also sinnvoll (solange wir einen guten Grund haben zu vermuten, dass der Zugriff auf die Speicheradresse 0xA000000
auf diese Weise gewonnen hat. t etwas in die Luft jagen). In C # ist dies nicht sinnvoll (wir können es in .NET fast erzwingen, aber mit der .NET-Speicherverwaltung ist es nicht sehr wahrscheinlich, dass Dinge verschoben werden). C # hat keine verifizierten Speicheradressen.
So, wie refied Mittel „made real“ ein „verdinglicht Typ“ ist eine Art können wir „Rede über“ in der Sprache , in Frage.
Bei Generika bedeutet dies zwei Dinge.
Eines ist, dass List<string>
es sich um einen Typ handelt, der genauso ist string
oder int
ist. Wir können diesen Typ vergleichen, seinen Namen erhalten und uns danach erkundigen:
Console.WriteLine(typeof(List<string>).FullName); // System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Console.WriteLine(typeof(List<string>) == (42).GetType()); // False
Console.WriteLine(typeof(List<string>) == Enumerable.Range(0, 1).Select(i => i.ToString()).ToList().GetType()); // True
Console.WriteLine(typeof(List<string>).GenericTypeArguments[0] == typeof(string)); // True
Dies hat zur Folge, dass wir über die Parametertypen einer generischen Methode (oder einer Methode einer generischen Klasse) innerhalb der Methode selbst "sprechen" können:
public static void DescribeType<T>(T element)
{
Console.WriteLine(typeof(T).FullName);
}
public static void Main()
{
DescribeType(42); // System.Int32
DescribeType(42L); // System.Int64
DescribeType(DateTime.UtcNow); // System.DateTime
}
Zu viel zu tun ist in der Regel "stinkend", hat aber viele nützliche Fälle. Schauen Sie sich zum Beispiel an:
public static TSource Min<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
Comparer<TSource> comparer = Comparer<TSource>.Default;
TSource value = default(TSource);
if (value == null)
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
do
{
if (!e.MoveNext()) return value;
value = e.Current;
} while (value == null);
while (e.MoveNext())
{
TSource x = e.Current;
if (x != null && comparer.Compare(x, value) < 0) value = x;
}
}
}
else
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (!e.MoveNext()) throw Error.NoElements();
value = e.Current;
while (e.MoveNext())
{
TSource x = e.Current;
if (comparer.Compare(x, value) < 0) value = x;
}
}
}
return value;
}
Dies führt nicht viele Vergleiche zwischen dem Typ TSource
und verschiedenen Typen für unterschiedliche Verhaltensweisen durch (im Allgemeinen ein Zeichen dafür, dass Sie überhaupt keine Generika hätten verwenden sollen), aber es wird zwischen einem Codepfad für Typen aufgeteilt, die sein können null
(sollte null
if zurückgeben Kein Element gefunden und darf keine Vergleiche anstellen, um das Minimum zu finden, wenn eines der verglichenen Elemente ist null
) und den Codepfad für Typen, die nicht gefunden werden können null
(sollte geworfen werden, wenn kein Element gefunden wurde, und muss sich nicht um die Möglichkeit von null
Elementen kümmern ).
Da dies TSource
innerhalb der Methode "real" ist, kann dieser Vergleich entweder zur Laufzeit oder zur Jitting-Zeit durchgeführt werden (im Allgemeinen Jitting-Zeit, sicherlich würde der obige Fall dies zur Jitting-Zeit tun und keinen Maschinencode für den nicht genommenen Pfad erzeugen), und wir haben a separate "echte" Version der Methode für jeden Fall. (Als Optimierung wird der Maschinencode für verschiedene Methoden für verschiedene Parameter vom Typ Referenztyp gemeinsam genutzt, da dies nicht beeinflusst werden kann und wir daher die Menge des ausgestoßenen Maschinencodes reduzieren können.)
(Es ist nicht üblich , über Verdinglichung von generischen Typen in C # zu sprechen , wenn Sie auch mit Java beschäftigen, denn in C # wir diese Verdinglichung nehmen nur für selbstverständlich, alle Arten verdinglicht werden in Java, nicht-generischen Typen werden als. Verdinglicht , weil das ist eine Unterscheidung zwischen ihnen und generischen Typen).