型が特定のジェネリックインターフェイス型を実装しているかどうかを判断する方法


226

次のタイプ定義を想定します。

public interface IFoo<T> : IBar<T> {}
public class Foo<T> : IFoo<T> {}

マングル型のみが使用可能な場合にFoo、その型が汎用インターフェースを実装しているかどうかを確認するにはどうすればよいIBar<T>ですか?

回答:


387

TcKからの回答を使用することで、次のLINQクエリでも実行できます。

bool isBar = foo.GetType().GetInterfaces().Any(x =>
  x.IsGenericType &&
  x.GetGenericTypeDefinition() == typeof(IBar<>));

1
これは非常にエレガントなソリューションです!私がSOで見た他のものは、foreachループまたはより長いLINQクエリを使用します。ただし、これを使用するには、.NET Framework 3.5が必要です。
ダニエルT.

7
これを拡張メソッドのa.bit.ly/ccza8Bにすることをお勧めします-これはかなりきれいになります!
Brad Heller

1
必要に応じて、返されたインターフェイスで繰り返し実行する必要がある場合があります。
セバスチャングッド

4
これは.net内に実装されるべきだったと思います...コアとして... member.Implements(IBar)やCustomType.Implements(IBar)のように、あるいはもっと良いのは、キーワード "is"を使用することです。 ..私はc#を調査していますが、今は.netで少しがっかりしています...
Sofija

2
マイナーな追加:IBarに複数のジェネリック型がある場合、これを次のように示す必要がありtypeof(IBar<,,,>)ます:コンマをプレースホルダーのように機能させる
Rob Von Nesselrode

33

継承ツリーを上に移動して、ツリー内の各クラスのすべてのインターフェースを見つけ、インターフェースが汎用である場合typeof(IBar<>)は呼び出しの結果と比較するType.GetGenericTypeDefinition 必要があります。それは確かに少し苦痛です。

詳細とコードについては、この回答これらの回答を参照してください。


なぜIBar <SomeClass>にキャストしてnullをチェックしないのですか?(もちろん、「as」でキャストすることを意味します)
Pablo Retyk

5
Tは不明であり、特定の型にキャストすることはできません。
sduplooy 2009

@sduplooy:Tがどのようにして不明になる可能性があるのか​​、私は見当たらないでしょうか?パブリッククラスFooをコンパイルします:IFoo <T> {}
Pablo Retyk

25
public interface IFoo<T> : IBar<T> {}
public class Foo : IFoo<Foo> {}

var implementedInterfaces = typeof( Foo ).GetInterfaces();
foreach( var interfaceType in implementedInterfaces ) {
    if ( false == interfaceType.IsGeneric ) { continue; }
    var genericType = interfaceType.GetGenericTypeDefinition();
    if ( genericType == typeof( IFoo<> ) ) {
        // do something !
        break;
    }
}

2
typeof(Foo)はSystem.Typeオブジェクト(Fooについて説明)を返すため、GetType()呼び出しは常にSystem.Typeのタイプを返します。typeof(Foo).GetInterfaces()に変更する必要があります
Michael Meadows

9

ヘルパーメソッド拡張として

public static bool Implements<I>(this Type type, I @interface) where I : class
{
    if(((@interface as Type)==null) || !(@interface as Type).IsInterface)
        throw new ArgumentException("Only interfaces can be 'implemented'.");

    return (@interface as Type).IsAssignableFrom(type);
}

使用例:

var testObject = new Dictionary<int, object>();
result = testObject.GetType().Implements(typeof(IDictionary<int, object>)); // true!

2
「IsAssignableFrom」はまさに私が探していたものでした
Jesper

22
これは、ジェネリック型パラメーターを知らないという要求者の要件に対しては機能しません。あなたの例からtestObject.GetType()。Implements(typeof(IDictionary <、>)); falseを返します。
ctusch 2012

@ctuschその後、そのための解決策はありますか?
Tohid

5

@GenericProgrammers拡張メソッドの少し単純なバージョンを使用しています。

public static bool Implements<TInterface>(this Type type) where TInterface : class {
    var interfaceType = typeof(TInterface);

    if (!interfaceType.IsInterface)
        throw new InvalidOperationException("Only interfaces can be implemented.");

    return (interfaceType.IsAssignableFrom(type));
}

使用法:

    if (!featureType.Implements<IFeature>())
        throw new InvalidCastException();

5
それでも、元の質問の一般的なインターフェースに対する要件に従って機能しません。
nathanchere

4

ジェネリックインターフェイスの構築型に対してチェックする必要があります。

あなたはこのようなことをする必要があります:

foo is IBar<String>

なぜならIBar<String>、その構築型を表すからです。これを行う必要があるのTは、チェックでが定義されていない場合、コンパイラは、IBar<Int32>またはのどちらであるかを認識できないためですIBar<SomethingElse>


4

完全型システムに取り組むために、私は例えば、あなたは再帰を処理する必要があると思うIList<T>ICollection<T>IEnumerable<T>、それなしあなたがそれを知っているではないでしょうIList<int>、最終的に実装しますIEnumerable<>

    /// <summary>Determines whether a type, like IList&lt;int&gt;, implements an open generic interface, like
    /// IEnumerable&lt;&gt;. Note that this only checks against *interfaces*.</summary>
    /// <param name="candidateType">The type to check.</param>
    /// <param name="openGenericInterfaceType">The open generic type which it may impelement</param>
    /// <returns>Whether the candidate type implements the open interface.</returns>
    public static bool ImplementsOpenGenericInterface(this Type candidateType, Type openGenericInterfaceType)
    {
        Contract.Requires(candidateType != null);
        Contract.Requires(openGenericInterfaceType != null);

        return
            candidateType.Equals(openGenericInterfaceType) ||
            (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition().Equals(openGenericInterfaceType)) ||
            candidateType.GetInterfaces().Any(i => i.IsGenericType && i.ImplementsOpenGenericInterface(openGenericInterfaceType));

    }

3

まず、public class Foo : IFoo<T> {}Tの代わりにクラスを指定する必要があるため、コンパイルは行われませんが、次のようなことを想定しています。public class Foo : IFoo<SomeClass> {}

その後、あなたがするなら

Foo f = new Foo();
IBar<SomeClass> b = f as IBar<SomeClass>;

if(b != null)  //derives from IBar<>
    Blabla();

2

ジェネリックベースタイプとインターフェイスをサポートする拡張メソッドが必要な場合は、sduplooyの答えを拡張しました。

    public static bool InheritsFrom(this Type t1, Type t2)
    {
        if (null == t1 || null == t2)
            return false;

        if (null != t1.BaseType &&
            t1.BaseType.IsGenericType &&
            t1.BaseType.GetGenericTypeDefinition() == t2)
        {
            return true;
        }

        if (InheritsFrom(t1.BaseType, t2))
            return true;

        return
            (t2.IsAssignableFrom(t1) && t1 != t2)
            ||
            t1.GetInterfaces().Any(x =>
              x.IsGenericType &&
              x.GetGenericTypeDefinition() == t2);
    }

1

型がジェネリック型を継承または実装しているかどうかを確認するメソッド:

   public static bool IsTheGenericType(this Type candidateType, Type genericType)
    {
        return
            candidateType != null && genericType != null &&
            (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == genericType ||
             candidateType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == genericType) ||
             candidateType.BaseType != null && candidateType.BaseType.IsTheGenericType(genericType));
    }

0

次の拡張機能を試してください。

public static bool Implements(this Type @this, Type @interface)
{
    if (@this == null || @interface == null) return false;
    return @interface.GenericTypeArguments.Length>0
        ? @interface.IsAssignableFrom(@this)
        : @this.GetInterfaces().Any(c => c.Name == @interface.Name);
}

それをテストする。つくる

public interface IFoo { }
public interface IFoo<T> : IFoo { }
public interface IFoo<T, M> : IFoo<T> { }
public class Foo : IFoo { }
public class Foo<T> : IFoo { }
public class Foo<T, M> : IFoo<T> { }
public class FooInt : IFoo<int> { }
public class FooStringInt : IFoo<string, int> { }
public class Foo2 : Foo { }

そしてテスト方法

public void Test()
{
    Console.WriteLine(typeof(Foo).Implements(typeof(IFoo)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<>)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<int>)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<string>)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<,>)));
    Console.WriteLine(typeof(FooStringInt).Implements(typeof(IFoo<,>)));
    Console.WriteLine(typeof(FooStringInt).Implements(typeof(IFoo<string,int>)));
    Console.WriteLine(typeof(Foo<int,string>).Implements(typeof(IFoo<string>)));
 }

-1

以下に問題はありません。

bool implementsGeneric = (anObject.Implements("IBar`1") != null);

IBarクエリで特定のジェネリック型パラメーターを提供する場合は、追加のクレジットとしてAmbiguousMatchExceptionをキャッチできます。


まあ、可能であれば文字列リテラルを使わないほうがいいです。IBarインターフェイスの名前を変更しても文字列リテラルは変更されず、エラーは実行時にのみ検出されるため、このアプローチではアプリケーションのリファクタリングが難しくなります。
アンディロシュ

「マジックストリング」などの使用に関する上記のコメントに通常同意する限り、これは私が見つけた最善のアプローチです。十分に近い-"IWhatever`1"に等しいPropertyType.Nameのテスト。
nathanchere

これはなぜですか?bool implementsGeneric = (anObject.Implements(typeof(IBar<>).Name) != null);
MaximeGélinas2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.