2016-07-04 3 views
4

ПРИМЕЧАНИЯ: Этого вопрос о техническом аспекте, и НЕ о проектных решениях. Ответ или комментарий о другом дизайне НЕ Ответьте на этот вопрос, так как меня интересует только природа технического аспекта этого конкретного вопроса.GenericTypeDefinitionName от IEnumerable <object>

В C# 6.0 У меня есть этот метод, где я пропускании IEnumerable<T>:

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

Допустим, абонент звонит его на MyClass[] массиве.
То, что я хочу, это имя класса определения общего типа, у меня была эта реализация метода:

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

Теперь в этом случае (внутри тела метода), это правильно возвращает название класс (например, строка "MyClass"). Так что я попытался реорганизовать это в открытый общий метод расширения, как так:

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

И затем вызвать GetGenericTypeDefinitionName() изнутри MyMethod() так:

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

Который также работает, но потом я понял: "Привет! Почему не толькоreturn typeof(T).Name? '

Который получился, возвращает строку "Object", в то время как я ожидал того же результата (например, "MyClass") в качестве предыдущей реализации.

Возможно ли получить ожидаемый тип? Кажется, вся информация теряется при работе с открытым типом общего типа T. И почему я не получаю ожидаемый тип? Каковы технические детали этого поведения для C#?

+0

Можете ли вы показать, как именно вы назвали его так, что 'TypeOf (T) .Name' вернулся' "объект" '? –

+0

Вы используете случайную дисперсию? 'IEnumerable ' может быть присвоен 'IEnumerable '. –

+0

@ RenéVogt Я отредактировал вопрос, чтобы показать, как и где вызывается метод расширения. – QuantumHive

ответ

5

То, что вы описали, произойдет, если вы используете общую дисперсию для назначения IEnumerable<T> некоторого ссылочного типа T на IEnumerable<object>.

Компилятор собирается использовать , что он знает выбрать <T> во время компиляции, что в этом случае object. Однако, если вы используете рефлексию, вы получите исходное имя - потому что фактический объект не изменился при помощи трюка.

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

Это потому, что вы на самом деле вызывая

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

оба будут писать на консольном реальном типе? –

+0

@ Ehsan Я добавил выходы консоли к примеру в редакции –

+0

Я думаю, что понимаю. Так как в моем вопросе 'MyMethod' имеет определенный тип времени компиляции (' IEnumerable ') в его аргументах, пример' GetByGenerics' никогда не будет знать оригинальный тип ('IEnumerable ') на основе открытого типа типа 'T' , верный? Потому что это определено во время компиляции и не может быть выведено компилятором? – QuantumHive

Смежные вопросы