Сравнение System.Type
От: vorona  
Дата: 09.09.16 11:19
Оценка:
Почему сравнение по типу возвращает false, а по токену true;
MethodInfo openMethod = typeof(Enumerable).GetMethods().First(m => m.Name == nameof(Enumerable.Select));
bool type = openMethod.ReturnType == typeof(IEnumerable<>);
bool token = openMethod.ReturnType.MetadataToken == typeof(IEnumerable<>).MetadataToken;
Re: Сравнение System.Type
От: Sinix  
Дата: 09.09.16 11:59
Оценка: 5 (1)
Здравствуйте, vorona, Вы писали:

V>Почему сравнение по типу возвращает false, а по токену true;


Ну, я могу сказать что пошло не так, но объяснения у меня пока нет.
        class Typed<T> { }

        class Some
        {
            public static Typed<T> Method<T>() => null;
        }


        static void Main(string[] args)
        {
            var m = typeof(Some).GetMethod(nameof(Some.Method));
            var typeDefinition = typeof(Typed<>);


            Console.WriteLine("Reflected method is generic definition: {0}", m.IsGenericMethodDefinition);            // true
            Console.WriteLine("Return type is generic definition:      {0}", m.ReturnType.IsGenericTypeDefinition);   // false, no idea why
            Console.WriteLine("typeof() result is generic definition:  {0}", typeDefinition.IsGenericTypeDefinition); // true

            Console.WriteLine("done.");
            Console.ReadKey();
        }


Выглядит как хороший вопрос для StackOverflow


UPD А, ну да, всё логично, это я стормозил. Как обычно, используем подход "что было бы, если?".

Итак, предположим, что для метода Typed<T> Method1<T>() return type был бы unbound-генерик-тип Typed<>. Т.е. .ReturnType.IsGenericTypeDefinition был бы true.
Неожиданно, ну да фиг с ним.

Проблема в другом: что должен возвращать returntype для метода с вот такой вот сигнатурой: T Method2<T>()?
Придётся вернуть тип-заглушку, не null же
Так вот, это "непонятно что вернуть, но должно быть T" используется и во всех остальных вариантах, для Method1 — как генерик-параметр для return type. Т.е. return type уже не unbound и .ReturnType.IsGenericTypeDefinition становится false, что мы и наблюдаем.

Чтоб точно не осталось вопросов: какой тип возвращаемого значения должен быть для Typed<T, int> Method3<T>(), если учитывать, что CLR не разрешает partial-bound generics?

P.S. Для любителей покопаться в кишках и освежить память:
https://alexandrnikitin.github.io/blog/dotnet-generics-under-the-hood/
http://www.codeproject.com/Articles/20481/NET-Type-Internals-From-a-Microsoft-CLR-Perspecti#21
http://joeduffyblog.com/2011/10/23/on-generics-and-some-of-the-associated-overheads/
Отредактировано 09.09.2016 13:10 Sinix . Предыдущая версия .
Re: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 12:47
Оценка: 46 (3) +1
Здравствуйте, vorona, Вы писали:

V>Почему сравнение по типу возвращает false, а по токену true;

V>
V>MethodInfo openMethod = typeof(Enumerable).GetMethods().First(m => m.Name == nameof(Enumerable.Select));
V>bool type = openMethod.ReturnType == typeof(IEnumerable<>);
V>bool token = openMethod.ReturnType.MetadataToken == typeof(IEnumerable<>).MetadataToken;
V>


Вообще openMethod.ReturnType <> typeof(IEnumerable<>);
Так как он выведен из IEnumerable<> и ParameterType.IsConstructedGenericType возвращает истина

Правильно будет
openMethod.ReturnType.GetGenericTypeDefinition() == typeof(IEnumerable<>)


А вообще вот функция
  public static bool IsGenericTypeOf(this Type t, TypeInfo TI, Type genericDefinition)
        {
            Type InterfaceType;
            return IsGenericTypeOf(t, genericDefinition, TI, out InterfaceType);

        }
        public static bool IsGenericTypeOf(this Type t, Type genericDefinition, TypeInfo TI, out Type InterfaceType)
        {
            InterfaceType = null;
            if (t == null) return false;



            if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == genericDefinition.GetGenericTypeDefinition()) return true;

            if (t.GetTypeInfo().BaseType != null)
            {
                var BT = t.GetTypeInfo().BaseType;
                if (BT != null && BT.IsGenericTypeOf(genericDefinition, TI, out InterfaceType))
                {
                    if (InterfaceType == null) InterfaceType = BT;
                    return true;
                }
            }

            if (TI.IsInterface)
            {

                foreach (var i in t.GetTypeInfo().GetInterfaces())
                {
                    if (i.IsGenericTypeOf(genericDefinition, TI, out InterfaceType))
                    {
                        if (InterfaceType == null) InterfaceType = i;
                        return true;

                    }
                }
            }


            return false;
        }



Вот поиск типов расширений Linq


static Type[] ПолучитьТипыРасширенийLinq()
        {
            var ТипПеречислителя = typeof(IEnumerable<>);
            var assembly = (typeof(System.Linq.Enumerable)).GetTypeInfo().Assembly;
            var query = from type in assembly.GetTypes()
                        let TI = type.GetTypeInfo()
                        where TI.IsSealed && TI.IsAbstract
                        from method in TI.GetMethods()
                        where method.IsStatic && method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false) && method.IsGenericMethod
                        let ParameterType = method.GetParameters()[0].ParameterType
                        where ParameterType.IsConstructedGenericType && ParameterType.GetGenericTypeDefinition() == ТипПеречислителя

                        select type;


            return query.Distinct().ToArray();
        }
и солнце б утром не вставало, когда бы не было меня
Отредактировано 09.09.2016 13:03 Serginio1 . Предыдущая версия . Еще …
Отредактировано 09.09.2016 13:02 Serginio1 . Предыдущая версия .
Отредактировано 09.09.2016 12:52 Serginio1 . Предыдущая версия .
Отредактировано 09.09.2016 12:50 Serginio1 . Предыдущая версия .
Re[2]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 13:01
Оценка:
Здравствуйте, Sinix, Вы писали:


Там еще одна особенность

public static bool ПодходитДженерикПараметр(Type ДженерикТип, Type тип)
        {

            var TI = ДженерикТип.GetTypeInfo();

            if (ДженерикТип.IsConstructedGenericType && TI.ContainsGenericParameters)
                return тип.IsGenericTypeOf(TI, ДженерикТип);

            if (!TI.IsGenericParameter) return false;

            bool ЕстьОграничения = false;
            Type[] tpConstraints = TI.GetGenericParameterConstraints();
            foreach (Type tpc in tpConstraints)
            {
                ЕстьОграничения = true;
                var tpcTI = tpc.GetTypeInfo();

                if (tpcTI.ContainsGenericParameters)
                {
                    if (ПодходитДженерикПараметр(tpc,тип)) return true;

                }
                else if (tpc.IsAssignableFrom(тип)) return true;

            }

            if (ЕстьОграничения) return false;



            return ПроверитьПараметрНаОграничения(TI, тип.GetTypeInfo());
        }


Есть типы которые содержат ограничения и для них
TI.GetGenericParameterConstraints() будет возвращать истину
а
IsConstructedGenericType ложь
и солнце б утром не вставало, когда бы не было меня
Re[3]: Сравнение System.Type
От: Sinix  
Дата: 09.09.16 13:19
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Там еще одна особенность


Огромная просьба: пишете код для других — используйте нормальные соглашения по именованию. А то хираганой отвечать буду ;P

S> Есть типы которые содержат ограничения и для них

S>TI.GetGenericParameterConstraints() будет возвращать истину
S>а
S>IsConstructedGenericType ложь

Ну так by design. .GetGenericParameterConstraints() работает только для generic arguments.

Т.е. для
    class Typed<T> where T: struct,IEquatable<T> { }


* typeof(Typed<>) - unboung generic type
* typeof(Typed<>).GetGenericArguments()[0] - generic type arg (тот самый T)
* typeof(Typed<>).GetGenericArguments()[0].GetGenericParameterConstraints() - ограничения generic type arg


по-хорошему для каждого из трёх надо бы заводить свой тип, чтоб не путаться, но тут уже поздно метаться.
Re[4]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 13:35
Оценка:
Здравствуйте, Sinix, Вы писали:



S>Т.е. для

S>
S>    class Typed<T> where T: struct,IEquatable<T> { }
S>


S>
S>* typeof(Typed<>) - unboung generic type
S>* typeof(Typed<>).GetGenericArguments()[0] - generic type arg (тот самый T)
S>* typeof(Typed<>).GetGenericArguments()[0].GetGenericParameterConstraints() - ограничения generic type arg
S>


S>по-хорошему для каждого из трёх надо бы заводить свой тип, чтоб не путаться, но тут уже поздно метаться.


Прошу прощения ошибся

if (ДженерикТип.IsConstructedGenericType && TI.ContainsGenericParameters)

То есть для
void Чтототам<T>(IEquatable<T> парам)

Выдаст для парам
ДженерикТип.IsConstructedGenericType истина
TI.ContainsGenericParameters истина

а для
void Чтототам<T>(T парам) where T:КакойтоТамОбъект
ДженерикТип.IsConstructedGenericType ложь
TI.ContainsGenericParameters истина
и солнце б утром не вставало, когда бы не было меня
Re[5]: Сравнение System.Type
От: Sinix  
Дата: 09.09.16 14:03
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>а для

S>void Чтототам<T>(T парам) where T:КакойтоТамОбъект
S>ДженерикТип.IsConstructedGenericType ложь
S>TI.ContainsGenericParameters истина

by design, см справку на IsConstructedGenericType и на IsGenericParameter.
Re[6]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 14:07
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>а для

S>>void Чтототам<T>(T парам) where T:КакойтоТамОбъект
S>>ДженерикТип.IsConstructedGenericType ложь
S>>TI.ContainsGenericParameters истина

S>by design, см справку на IsConstructedGenericType и на IsGenericParameter.

IsGenericParameter Это немного другое.

Для
void Чтототам<T>(IEquatable<T> парам)


Для парам IsGenericParameter = ложь

А вот
ContainsGenericParameters = true
и
IsConstructedGenericType = true
и солнце б утром не вставало, когда бы не было меня
Re[7]: Сравнение System.Type
От: Sinix  
Дата: 09.09.16 14:32
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>>by design, см справку на IsConstructedGenericType и на IsGenericParameter.

S> IsGenericParameter Это немного другое.

Ну так ещё раз смотрим справку, там расписано, какое свойство за что отвечает.
ContainsGenericParameters:

true if the Type object is itself a generic type parameter or has type parameters for which specific types have not been supplied; otherwise, false.

Поэтому его, как и кучу других свойств/методов используют только в паре с IsGenericType

Для всех остальных тож расписано, включая рекомендации по использованию типа

Use the System.Type.IsGenericType property to determine whether a System.Type object represents a generic type. Use the System.Type.ContainsGenericParameters property to determine whether a T:System.Type object represents an open constructed type or a closed constructed type.


IsConstructedGenericType для T не может быть true, т.к. для него IsGenericType == false.
Re[8]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 14:41
Оценка:
Здравствуйте, Sinix, Вы писали:



S>IsConstructedGenericType для T не может быть true, т.к. для него IsGenericType == false.


У меня такое условие

IsGenericType = (TI.IsGenericType && TI.IsGenericTypeDefinition) || TI.ContainsGenericParameters;
и солнце б утром не вставало, когда бы не было меня
Re[8]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 14:46
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>>by design, см справку на IsConstructedGenericType и на IsGenericParameter.

S>> IsGenericParameter Это немного другое.

S>Ну так ещё раз смотрим справку, там расписано, какое свойство за что отвечает.

S>ContainsGenericParameters:
S>

S>true if the Type object is itself a generic type parameter or has type parameters for which specific types have not been supplied; otherwise, false.

S>Поэтому его, как и кучу других свойств/методов используют только в паре с IsGenericType

S>Для всех остальных тож расписано, включая рекомендации по использованию типа

S>

S>Use the System.Type.IsGenericType property to determine whether a System.Type object represents a generic type. Use the System.Type.ContainsGenericParameters property to determine whether a T:System.Type object represents an open constructed type or a closed constructed type.


S>IsConstructedGenericType для T не может быть true, т.к. для него IsGenericType == false.

Для T нет, а для IEnumerable<T> может
Сейчас лень в понедельник проверю.
и солнце б утром не вставало, когда бы не было меня
Re[9]: Сравнение System.Type
От: Sinix  
Дата: 09.09.16 14:51
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> У меня такое условие

S>IsGenericType = (TI.IsGenericType && TI.IsGenericTypeDefinition) || TI.ContainsGenericParameters;

Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?
Re[10]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 14:57
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>> У меня такое условие

S>>IsGenericType = (TI.IsGenericType && TI.IsGenericTypeDefinition) || TI.ContainsGenericParameters;

S>Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?



Для Typed<T>
TI.ContainsGenericParameters будет истина
и солнце б утром не вставало, когда бы не было меня
Re[11]: Сравнение System.Type
От: Sinix  
Дата: 09.09.16 16:14
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>>Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?


S> Для Typed<T>

S>TI.ContainsGenericParameters будет истина
Глючит
Re[12]: Сравнение System.Type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.09.16 16:35
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>>Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?


S>> Для Typed<T>

S>>TI.ContainsGenericParameters будет истина
S>Глючит
https://msdn.microsoft.com/ru-ru/library/system.type.containsgenericparameters(v=vs.90).aspx
Просто в .Net Core

и typed<T> для TElement
public static Task<IDocument> ApiExtensions.NavigateAsync<TElement>(this TElement element) where TElement : IUrlUtilities, IElement;


ContainsGenericParameters вернет истина.

Поэтому я был немного удивлен такому поведению.
и солнце б утром не вставало, когда бы не было меня
Re: Сравнение System.Type
От: nikov США http://www.linkedin.com/in/nikov
Дата: 09.09.16 22:09
Оценка: 84 (6)
Здравствуйте, vorona, Вы писали:

V>Почему сравнение по типу возвращает false, а по токену true;

V>
V>MethodInfo openMethod = typeof(Enumerable).GetMethods().First(m => m.Name == nameof(Enumerable.Select));
V>bool type = openMethod.ReturnType == typeof(IEnumerable<>);
V>bool token = openMethod.ReturnType.MetadataToken == typeof(IEnumerable<>).MetadataToken;
V>


Потому что у этих generic типов разные типы-аргументы. У первого в качестве типа-аргумента используется тип-параметр T из декларации generic метода Select, а у второго — тип-параметр T из декларации generic типа IEnumerable<T>. Эти типы-параметры никак не связаны и даже объявлены в разных сборках. То, что у них одинаковое имя T — это совпадение.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

class Program
{
    static void Main()
    {
        var openMethod = typeof(Enumerable).GetMethods().First(m => m.Name == nameof(Enumerable.Select));
        var returnType = openMethod.ReturnType;
        var openType = typeof(IEnumerable<>);
        Console.WriteLine(returnType == openType); // False

        var typeArgument1 = returnType.GetGenericArguments()[0];
        var typeArgument2 = openType.GetGenericArguments()[0];
        Console.WriteLine(typeArgument1 == typeArgument2); // False

        Console.WriteLine(typeArgument1.DeclaringMethod == openMethod); // True
        Console.WriteLine(typeArgument2.DeclaringMethod == null); // True
        Console.WriteLine(typeArgument2.DeclaringType == openType); // True

        Console.WriteLine(typeArgument1.Assembly); // System.Core
        Console.WriteLine(typeArgument2.Assembly); // mscorlib
    }
}


Токен для generic типа не зависит от его типов-аргументов, он связан с его декларацией, поэтому он одинаковый.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.