10

リフレクションを使用してメソッドのパラメーターの型を検査するメソッドに取り組んでいます。このメソッドは、ParameterInfo を反復処理し、それらのパラメーターの型で何かを行っています。

TypeInfo.IsClass私は常に、 if is true、この型はクラスであり、常に型から (間接的に) 派生する (もちろん型自体がobject型である場合を除く)という仮定の下にありました。objectしたがって、TypeInfo.IsClassが true の場合は、TypeInfo.BaseType設定する必要があります。

うーん、私の仮定は間違っていました!type から派生しないクラスがありますobject。そして、私の仮定は私のコードを台無しにしました。

例えば:

Type type = typeof(int).MakeByRefType();

type.IsClasstrueなりtype.BaseTypeますnull

考えてみれば、それは論理的です。をチェックすることで、コードのクラッシュを防ぐことができますTypeInfo.IsByRef

objectここで私の質問は次のとおりです。クラス ( IsClass == true) であるが基本型 ( ) を持たないような「エキゾチックな」型 (ByRef 型と型以外) はありBaseType == nullますか?

答える前に: 私はIsClass == true! そして、タイプを使用した私の例intは単なる例です。どんなタイプでも良かった。だからいいえ:

  • インターフェース
  • 構造体
  • 空所

これまでの回答:

  • ByRef タイプ ( T&): 質問で説明されているとおり。
  • ポインター型 ( T*): Mark Gravell によって発見されました。
4

3 に答える 3

14

IsClassここでは単に誤解を招くだけだと思います。それは述べています:

System.Type がクラスかどうかを示す値を取得します。つまり、値の型やインターフェイスではありません。

フラグに が含まれているかどうか、および であるかどうかをチェックしますInterfaceValueType

残念ながら、これよりも多くのものがあります。ポインターはマネージド型ではありません。by-ref はポインターに非常に似ています。ポインターはobjects ではありませんが、一般的に使用されるキャストは実際には逆参照/キャストです。などの直接ポインタなどにも同じことが当てはまりますint*

.NET のすべてがobject:)であるとは限りません

var baseType = typeof(int*).BaseType; // null
bool liesAndMoreLies = typeof(int*).IsClass; // true

Eric Lippert がこれについて詳しく説明しています。すべてがオブジェクトから派生するわけではありません。他の例をいくつか挙げています (たとえば、オープン ジェネリック型)。

于 2013-05-09T12:40:44.927 に答える
2

インスタンス化されていないジェネリック型を除いて、すべてがオブジェクトから派生しているわけではありません @ Mr. Lippert's blog by Mr. Gravell's good answerで指摘されています。要件を満たす他の型を自分で見つけることをお勧めします。

それでは、この問題を解決するためにゼロから始めましょう。まず、あなたが把握しようとしている型は、コア ランタイム ライブラリにある必要がありますmscorlib.dll

public static partial class MartinMulderExtensions {
    public static IEnumerable<Type> GetMscorlibTypes() {
        return
            from assembly in AppDomain.CurrentDomain.GetAssemblies()
            let name=assembly.ManifestModule.Name
            where 0==String.Compare("mscorlib.dll", name, true)
            from type in assembly.GetTypes()
            select type;
    }
}

そして、型には のMakeXXXXType()ようなメソッドがありますMakeByRefType()。ここでは、より多くの可能性を考慮します。つまり、 type または typesを返す任意のメソッドです。任意の型の引数に関する知識がないため、メソッドは引数をゼロと見なします。

partial class MartinMulderExtensions {
    public static IEnumerable<Type> GetRetrievableTypes(this Type type) {
        var typesArray=(
            from method in type.GetMethods()
            where 0==method.GetParameters().Count()

            let typeArray=
                method.InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(type)

            where null!=typeArray
            select typeArray).ToArray();

        var types=
            typesArray.Length>0
                ?typesArray.Aggregate(Enumerable.Union)
                :Type.EmptyTypes;

        return types.Union(new[] { type });
    }
}

ただし、の実装では、非ジェネリック型InvokeZeroArgumentMethodWhichReturnsTypeOrTypesでの呼び出しなど、この種の呼び出しの無効なケースがいくつかあります。try-catchGetGenericParameterConstraints()でこれらのケースを回避しています:

partial class MartinMulderExtensions {
    public static IEnumerable<Type>
        InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(
        this MethodInfo method, Type t
        ) {
        try {
            if(typeof(Type)==method.ReturnType) {
                var type=method.Invoke(t, null) as Type;

                if(null!=type)
                    return new[] { type };
            }

            if(typeof(Type[])==method.ReturnType) {
                var types=method.Invoke(t, null) as Type[];

                if(types.Length>0)
                    return types;
            }
        }
        catch(InvalidOperationException) {
        }
        catch(TargetInvocationException) {
        }
        catch(TargetException) {
        }

        return Type.EmptyTypes;
    }
}

そして今、目的のタイプを把握します。メソッドを段階的に構築しましょう。最初のステップは、考えられるすべてのタイプのスコープを定義することです。

partial class MartinMulderExtensions {
    public static Type[] GetDesiredTypes() {
        return (
            from type in MartinMulderExtensions.GetMscorlibTypes()
            .Select(x => x.GetRetrievableTypes())
            .Aggregate(Enumerable.Union)

次に、あなたが基本的に述べたことによると:

objectここで私の質問は次のとおりです。クラス ( IsClass == true) であるが基本型 ( ) を持たないような「エキゾチックな」型 (ByRef 型と型以外) はありBaseType == nullますか?

            where null==type.BaseType
            where type.IsClass

また、次のことも述べましたbefore answer

答える前に: 私はIsClass == true! そして、タイプを使用した私の例intは単なる例です。どんなタイプでも良かった。だからいいえ:

  • インターフェース
  • 構造体
  • 空所
            where !type.IsInterface
            where !type.IsValueType
            where typeof(void)!=type

最後のステップとして、既に回答されているものは飛ばしてメソッドを完成させましょう:

            where !type.IsByRef
            where !type.IsPointer
            select type
            ).ToArray();
    }
}

これで、呼び出しMartinMulderExtensions.GetDesiredTypes()て目的の型を取得できます。

public partial class TestClass {
    public static void TestMethod() {
        foreach(var type in MartinMulderExtensions.GetDesiredTypes())
            Console.WriteLine(type);
    }
}

完全なコードの場合:

public static partial class MartinMulderExtensions {
    public static IEnumerable<Type> GetMscorlibTypes() {
        return
            from assembly in AppDomain.CurrentDomain.GetAssemblies()
            let name=assembly.ManifestModule.Name
            where 0==String.Compare("mscorlib.dll", name, true)
            from type in assembly.GetTypes()
            select type;
    }

    public static IEnumerable<Type>
        InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(
        this MethodInfo method, Type t
        ) {
        try {
            if(typeof(Type)==method.ReturnType) {
                var type=method.Invoke(t, null) as Type;

                if(null!=type)
                    return new[] { type };
            }

            if(typeof(Type[])==method.ReturnType) {
                var types=method.Invoke(t, null) as Type[];

                if(types.Length>0)
                    return types;
            }
        }
        catch(InvalidOperationException) {
        }
        catch(TargetInvocationException) {
        }
        catch(TargetException) {
        }

        return Type.EmptyTypes;
    }

    public static IEnumerable<Type> GetRetrievableTypes(this Type type) {
        var typesArray=(
            from method in type.GetMethods()
            where 0==method.GetParameters().Count()

            let typeArray=
                method.InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(type)

            where null!=typeArray
            select typeArray).ToArray();

        var types=
            typesArray.Length>0
                ?typesArray.Aggregate(Enumerable.Union)
                :Type.EmptyTypes;

        return types.Union(new[] { type });
    }

    public static Type[] GetDesiredTypes() {
        return (
            from type in MartinMulderExtensions.GetMscorlibTypes()
            .Select(x => x.GetRetrievableTypes())
            .Aggregate(Enumerable.Union)

            where null==type.BaseType
            where type.IsClass
            where !type.IsInterface
            where !type.IsValueType
            where typeof(void)!=type

            where !type.IsByRef
            where !type.IsPointer

            select type
            ).ToArray();
    }
}
于 2013-05-11T02:31:04.693 に答える