web-dev-qa-db-ja.com

「オープン」ジェネリック型でIsAssignableFromを使用する

リフレクションを使用して、特定の基本クラスから継承するタイプのセットを見つけようとしています。単純な型を理解するのにそれほど時間はかかりませんでしたが、ジェネリックに関しては困惑しています。

このコードでは、最初のIsAssignableFromはtrueを返しますが、2番目はfalseを返します。それでも、最終的な割り当ては問題なくコンパイルされます。

class class1 { }
class class2 : class1 { }
class generic1<T> { }
class generic2<T> : generic1<T> { }

class Program
{
    static void Main(string[] args)
    {
        Type c1 = typeof(class1);
        Type c2 = typeof(class2);
        Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2));

        Type g1 = typeof(generic1<>);
        Type g2 = typeof(generic2<>);
        Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));

        generic1<class1> cc = new generic2<class1>();
    }
}

では、実行時に、あるジェネリック型定義が別のジェネリック型定義から派生したかどうかをどのように判断するのでしょうか?

56
ThatBlairGuy

別の質問への回答 から:

public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
    var interfaceTypes = givenType.GetInterfaces();

    foreach (var it in interfaceTypes)
    {
        if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
            return true;
    }

    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
        return true;

    Type baseType = givenType.BaseType;
    if (baseType == null) return false;

    return IsAssignableToGenericType(baseType, genericType);
}

(コードが私のものではないので、回答が気に入った場合は、リンクされた回答に投票してください。)

80
Konrad Rudolph

あなたが投稿した正確なコードは、驚くべき結果を返しません。

これは「false」を示します。

_Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
_

これは「true」を示します。

_Type g1 = typeof(generic1<class1>);
Type g2 = typeof(generic2<class1>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
_

違いは、オープンジェネリック型がインスタンスを持つことができないため、一方が他方に「割り当て可能」ではないことです。

docs から:

trueと現在のcが同じ型を表す場合、または現在のTypeTypeの継承階層にある場合、cを返します。 、または現在のTypecが実装するインターフェイスである場合、またはcがジェネリック型パラメーターであり、現在のTypeが制約の1つを表す場合cの。 falseは、これらの条件のいずれにも当てはまらない場合、またはcnullである場合。

この場合、明らかにこれらの条件のいずれも当てはまりません。また、追加の注意事項があります。

ジェネリック型の定義は、閉じた構築型から割り当てることはできません。つまり、閉じた構築型_MyGenericList<int>_(Visual BasicではMyGenericList(Of Integer))を型_MyGenericList<T>_の変数に割り当てることはできません。

11
Jon

次の場合、Konrad Rudolphが提供するメソッドを使用してください。IsAssignableToGenericType(typeof(A)、typeof(A <>)); // falseを返します。

ここがより良い答えだと思います

public static bool IsAssignableFrom(Type extendType, Type baseType)
{
    while (!baseType.IsAssignableFrom(extendType))
    {
        if (extendType.Equals(typeof(object)))
        {
            return false;
        }
        if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
        {
            extendType = extendType.GetGenericTypeDefinition();
        }
        else
        {
            extendType = extendType.BaseType;
        }
    }
    return true;
}

テストケース、詳細については、 C#ジェネリックでのIsAssignableFromの使用 を参照してください。

using System;

/**
 * Sam Sha - yCoder.com
 *
 * */
namespace Test2
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            string a = "ycoder";
            Console.WriteLine(a is object);
            A aa = new A();
            //Console.WriteLine(aa is A<>);//con't write code like this
            typeof(A<>).IsAssignableFrom(aa.GetType());//return false

            Trace(typeof(object).IsAssignableFrom(typeof(string)));//true
            Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false

            AAA aaa = new AAA();
            Trace("Use IsTypeOf:");
            Trace(IsTypeOf(aaa, typeof(A<>)));
            Trace(IsTypeOf(aaa, typeof(AA)));
            Trace(IsTypeOf(aaa, typeof(AAA<>)));

            Trace("Use IsAssignableFrom from stackoverflow - not right:");
            Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error
            Trace(IsAssignableFrom(typeof(AA), typeof(A<>)));
            Trace(IsAssignableFrom(typeof(AAA), typeof(A<>)));

            Trace("Use IsAssignableToGenericType:");
            Trace(IsAssignableToGenericType(typeof(A), typeof(A<>)));
            Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>)));
            Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>)));
        }

        static void Trace(object log){
                Console.WriteLine(log);
        }

        public static bool IsTypeOf(Object o, Type baseType)
        {
            if (o == null || baseType == null)
            {
                return false;
            }
            bool result = baseType.IsInstanceOfType(o);
            if (result)
            {
                return result;
            }
            return IsAssignableFrom(o.GetType(), baseType);
        }

        public static bool IsAssignableFrom(Type extendType, Type baseType)
        {
            while (!baseType.IsAssignableFrom(extendType))
            {
                if (extendType.Equals(typeof(object)))
                {
                    return false;
                }
                if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
                {
                    extendType = extendType.GetGenericTypeDefinition();
                }
                else
                {
                    extendType = extendType.BaseType;
                }
            }
            return true;
        }

        //from stackoverflow - not good enough
        public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
            var interfaceTypes = givenType.GetInterfaces();

            foreach (var it in interfaceTypes)
                if (it.IsGenericType)
                    if (it.GetGenericTypeDefinition() == genericType) return true;

            Type baseType = givenType.BaseType;
            if (baseType == null) return false;

            return baseType.IsGenericType &&
                baseType.GetGenericTypeDefinition() == genericType ||
                IsAssignableToGenericType(baseType, genericType);
        }
    }

    class A{}
    class AA : A{}
    class AAA : AA{}
}
2
sam sha

含まれているタイプを比較する必要があります。参照: ジェネリッククラスまたはメソッドのメンバーからTの型を取得する方法?

つまり、ジェネリッククラス自体ではなく、ジェネリッククラスに含まれている型が割り当て可能かどうかを確認する必要があると思います。

0
Nick

私はこの問題を解決する別のアプローチを持っています、ここに私のクラスがあります

public class Signal<T>{
   protected string Id {get; set;} //This must be here, I use a property because MemberInfo is returned in an array via GetMember() reflection function
   //Some Data and Logic And stuff that involves T
}

public class OnClick : Signal<string>{}

OnClick型のインスタンスを持っているが、それを知らない場合、Signal <>を継承する何かのインスタンスがあるかどうかを知りたい場合はどうすればよいですか?私はこれをします

Type type = GetTypeWhomISuspectMightBeAGenericSignal();

PropertyInfo secretProperty = type.GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance);

Type SpecificGenericType = secretProperty.DeclaringType; //This is the trick

bool IsMyTypeInheriting = SpecificGenericType.IsGenericType && SpecificGenericType.GetGenericTypeDefinition() == typeof(Signal<>); //This way we are getting the genericTypeDefinition and comparing it to any other genericTypeDefinition of the same argument length.

したがって、これは再帰的ではなく、私にとってはうまくいき、指定されたプロパティを介してトリックを使用します。すべてのジェネリックの割り当て可能性をチェックする関数を作成するのが難しいという制限があります。しかし、特定のタイプでは機能します

明らかに、if()条件をより良く確認する必要がありますが、これらは、この方法でベースジェネリックへの型の代入可能性を評価するために必要なRaw行です。

お役に立てれば

0
Helical