Здравствуйте, 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?
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();
}
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, 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
по-хорошему для каждого из трёх надо бы заводить свой тип, чтоб не путаться, но тут уже поздно метаться.
Здравствуйте, Serginio1, Вы писали:
S>а для S>void Чтототам<T>(T парам) where T:КакойтоТамОбъект S>ДженерикТип.IsConstructedGenericType ложь S>TI.ContainsGenericParameters истина
by design, см справку на IsConstructedGenericType и на IsGenericParameter.
Здравствуйте, 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
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, 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.
Здравствуйте, 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> может
Сейчас лень в понедельник проверю.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> У меня такое условие S>IsGenericType = (TI.IsGenericType && TI.IsGenericTypeDefinition) || TI.ContainsGenericParameters;
Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> У меня такое условие S>>IsGenericType = (TI.IsGenericType && TI.IsGenericTypeDefinition) || TI.ContainsGenericParameters;
S>Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?
Для Typed<T>
TI.ContainsGenericParameters будет истина
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>Если меня не глючит, то это условие сработает на Typed<>, и на T, но не на Typed<T>. Точно именно это нужно?
S> Для Typed<T> S>TI.ContainsGenericParameters будет истина
Глючит
Потому что у этих 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); // Falsevar 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 типа не зависит от его типов-аргументов, он связан с его декларацией, поэтому он одинаковый.