如何确定类型是否实现特定的通用接口类型


226

假定以下类型定义:

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

当只有整齐的类型可用时,如何确定该类型是否Foo实现了通用接口IBar<T>

Answers:


387

通过使用TcK的答案,也可以使用以下LINQ查询来完成:

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

1
这是一个非常优雅的解决方案!我在SO上看到的其他对象使用foreach循环或更长的LINQ查询。请记住,要使用此功能,您需要具有.NET Framework 3.5。
Daniel T.

7
我建议您将其作为la.bit / ly / ccza8B的扩展方法-可以很好地清理它!
布拉德·海勒

1
根据您的需要,您可能会发现需要重复使用返回的接口。
塞巴斯蒂安·古德

4
我想说这应该在.net内更好地实现……作为一个核心……像member.Implements(IBar)或CustomType.Implements(IBar),或者甚至更好,使用关键字“ is” .. ..我正在探索C#,现在对.net感到有些失望...
Sofija 2012年

2
较小的补充:如果IBar具有多个泛型类型,则需要这样表示:typeof(IBar<,,,>)逗号充当占位符
Rob Von Nesselrode

33

您必须遍历继承树,并在树中找到每个类的所有接口,如果接口是通用的,typeof(IBar<>)调用结果进行比较。当然,这有点痛苦。Type.GetGenericTypeDefinition

有关更多信息和代码,请参见此答案以及这些答案


为什么不将其转换为IBar <SomeClass>并检查是否为null?(我的意思是使用“ as”进行铸造)
Pablo Retyk

5
T是未知的,不能转换为特定类型。
sduplooy

@sduplooy:也许我错过了一些未知的东西?它将编译公共类Foo:IFoo <T> {}
Pablo Retyk,2009年

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,2010年

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在检查中未定义if ,则编译器将不知道您的意思是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接口不会更改字符串文字,并且错误只能在运行时检测到。
andyroschy

尽管我通常同意上面关于使用“魔力弦”等的评论,但这仍然是我发现的最佳方法。足够接近-测试PropertyType.Name等于“ IWhatever`1”。
nathanchere '16

为什么不呢?bool implementsGeneric = (anObject.Implements(typeof(IBar<>).Name) != null);
MaximeGélinas17年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.