2016-07-04 5 views
4

HINWEIS: Diese Frage zu einem technischen Aspekte ist, und NICHT über Design-Entscheidungen. Beantworten oder kommentieren über ein anderes Design tut nicht beantworten Sie diese Frage, da ich nur an der Art des technischen Aspekts dieser spezifischen Frage interessiert bin.GenericTypeDefinitionName von IEnumerable <object>

In C# 6.0 habe ich diese Methode, wo ich eine IEnumerable<T> bin vorbei:

public void MyMethod(IEnumerable<object> list) { ... } 

Lassen Sie uns sagen, dass der Anrufer ruft sie auf einem MyClass[] Array.
Was ich will, ist der Klassenname der generischen Typdefinition, hatte ich diese Implementierung des Verfahrens:

public void MyMethod(IEnumerable<object> list) 
{ 
    ... 
    var name = 
     from abstraction in list.GetType().GetInterfaces() 
     where abstraction.IsGenericType 
     && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>) 
     from genericArgumentType in abstraction.GetGenericArguments() 
     select genericArgumentType.Name; 
    ... 
} 

Jetzt in diesem Fall (im Inneren des Körpers des Verfahrens), diese korrekt gibt den Namen der Klasse (zB eine Zeichenkette "MyClass"). Also habe ich versucht, diese in eine offene generische Erweiterungsmethode Refactoring wie so:

public static string GetGenericTypeDefinitionName<T>(this IEnumerable<T> list) 
{ 
    var name = 
     from abstraction in list.GetType().GetInterfaces() 
     where abstraction.IsGenericType 
     && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>) 
     from genericArgumentType in abstraction.GetGenericArguments() 
     select genericArgumentType.Name; 

    return name.Single(); 
} 

Und dann GetGenericTypeDefinitionName() rufen aus MyMethod() etwa so:

public void MyMethod(IEnumerable<object> list) 
{ 
    ... 
    var name = list.GetGenericTypeDefinitionName(); 
    ... 
} 

Welche auch funktioniert, aber dann merkte ich: 'Hallo! Warum nicht einfachreturn typeof(T).Name? '

Welche stellte sich heraus, gibt es die Zeichenfolge "Object", während ich das gleiche Ergebnis erwartet (z. B. "MyClass") als die vorherige Implementierung.

Ist es überhaupt möglich, den erwarteten Typ zu erhalten? Es scheint, dass alle Informationen verloren gehen, wenn es um den offenen generischen Typ T geht. Warum bekomme ich auch nicht den erwarteten Typ? Was sind die technischen Details dieses speziellen Verhaltens für C#?

+0

Aufruf Können Sie zeigen, wie genau man es nannte, so dass 'typeof (T) .Name'' "Objekt" zurück '? –

+0

Verwenden Sie zufällig generische Varianz? Ein 'IEnumerable ' kann einem 'IEnumerable ' zugewiesen werden. –

+0

@ RenéVogt Ich habe die Frage bearbeitet, um zu zeigen, wie und wo die Erweiterungsmethode aufgerufen wird. – QuantumHive

Antwort

5

Was Sie passieren beschreiben, wenn Sie generische Varianz verwenden, um eine IEnumerable<T> einiger Referenztyp T zu einem IEnumerable<object> zuzuordnen.

Der Compiler verwenden wird, was es kennt die <T> zur Compile-Zeit zu holen, die object in diesem Fall ist. Wenn Sie jedoch Reflektion verwenden, erhalten Sie den ursprünglichen Namen - weil das tatsächliche Objekt unverändert durch Varianz Trick ist.

static void Main() 
{ 
    IEnumerable<Foo> typed = new Foo[0]; 
    IEnumerable<object> untyped = typed; 

    Console.WriteLine(typed.GetByGenerics());  // Foo 
    Console.WriteLine(untyped.GetByGenerics()); // Object 

    Console.WriteLine(typed.GetByReflection()); // Foo 
    Console.WriteLine(untyped.GetByReflection()); // Foo 
} 
public static string GetByGenerics<T>(this IEnumerable<T> list) 
{ 
    return typeof(T).Name; 
} 
public static string GetByReflection<T>(this IEnumerable<T> list) 
{ 
    var name = 
     from abstraction in list.GetType().GetInterfaces() 
     where abstraction.IsGenericType 
     && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>) 
     from genericArgumentType in abstraction.GetGenericArguments() 
     select genericArgumentType.Name; 

    return name.Single(); 
} 

Dies liegt daran, Sie tatsächlich

Console.WriteLine(GetByGenerics<Foo>(typed));  // Foo 
Console.WriteLine(GetByGenerics<Object>(untyped)); // Object 
+0

beide werden auf Konsole tatsächlichen Typ schreiben? –

+0

@Ehsan Ich habe die Konsolenausgaben zum Beispiel in der Bearbeitung hinzugefügt –

+0

Ich denke, ich verstehe. Da in meiner Frage 'MyMethod' in ihren Argumenten ein Kompilierzeittyp definiert ist (' IEnumerable '), wird das 'GetByGenerics'-Beispiel niemals den ursprünglichen Typ (' IEnumerable ') basierend auf dem offenen generischen Typ 'T' kennen , richtig? Weil dies zur Kompilierzeit definiert ist und nicht vom Compiler abgeleitet werden kann? – QuantumHive