简体   繁体   English

如何确定类型是否使用 C# 反射实现接口

[英]How to determine if a type implements an interface with C# reflection

Does reflection in C# offer a way to determine if some given System.Type type models some interface? C#反射是否提供了一种方法来确定某些给定的System.Type类型是否为某些接口建模?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);

You have a few choices:你有几个选择:

  1. typeof(IMyInterface).IsAssignableFrom(typeof(MyType))

  2. typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))

For a generic interface, it's a bit different.对于通用接口,它有点不同。

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))

使用Type.IsAssignableFrom

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
public static bool ImplementsInterface(this Type type, Type ifaceType) 
{
    Type[] intf = type.GetInterfaces();
    for(int i = 0; i < intf.Length; i++) 
    {
        if(intf[ i ] == ifaceType) 
        {
            return true;
        }
    }
    return false;
}

I think this is the correct release, for three reasons:我认为这是正确的版本,原因有以下三个:

  1. It uses GetInterfaces and not IsAssignableFrom, it's faster since IsAssignableFrom eventually after several checks does call GetInterfaces.它使用 GetInterfaces 而不是 IsAssignableFrom,它更快,因为 IsAssignableFrom 最终在多次检查后确实调用了 GetInterfaces。
  2. It iterates over the local array, so there will be no bounds checks.它遍历本地数组,因此不会进行边界检查。
  3. It uses the == operator which is defined for Type, so probably is safer than the Equals method (that the Contains call, will eventually use).它使用为 Type 定义的 == 运算符,因此可能比 Equals 方法(包含调用最终会使用)更安全。

I just did:我刚做了:

public static bool Implements<I>(this Type source) where I : class
{
  return typeof(I).IsAssignableFrom(source);
}

I wish I could have said where I : interface , but interface is not a generic parameter constraint option.我希望我能说where I : interface ,但interface不是通用参数约束选项。 class is as close as it gets. class是尽可能接近。

Usage:用法:

if(MyType.Implements<IInitializable>())
  MyCollection.Initialize();

I just said Implements because that's more intuitive.我只是说Implements因为它更直观。 I always get IsAssignableFrom flip-flopped.我总是让IsAssignableFrom失败。

As someone else already mentioned: Benjamin Apr 10 '13 at 22:21"正如其他人已经提到的:本杰明 2013 年 4 月 10 日 22:21"

It sure was easy to not pay attention and get the arguments for IsAssignableFrom backwards.很容易不注意并且向后获取 IsAssignableFrom 的参数。 I will go with GetInterfaces now :p –我现在将使用 GetInterfaces :p –

Well, another way around is just to create a short extension method that fulfills, to some extent, the "most usual" way of thinking (and agreed this is a very little personal choice to make it slightly "more natural" based on one's preferences):嗯,另一种方法是创建一个简短的扩展方法,在某种程度上满足“最常见”的思维方式(并同意这是一个非常小的个人选择,根据个人喜好使其稍微“更自然” ):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }
}

And why not going a bit more generic (well not sure if it is really that interesting, well I assume I'm just passing another pinch of 'syntaxing' sugar):为什么不更通用一点(不知道它是否真的那么有趣,我想我只是通过了另一撮“语法”糖):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }

    public static bool IsAssignableTo<TAssignable>(this Type type)
    {
        return IsAssignableTo(type, typeof(TAssignable));
    }
}

I think it might be much more natural that way, but once again just a matter of very personal opinions:我认为这样可能更自然,但再次只是个人意见的问题:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();

Modifying Jeff's answer for optimal performance (thanks to performance test by Pierre Arnaud):修改 Jeff 的答案以获得最佳性能(感谢 Pierre Arnaud 的性能测试):

var type = typeof(MyType);
var implementsInterface = typeof(IMyInterface).IsAssignableFrom(type) && type.IsClass;

To find all types that implement an interface in a given Assembly :要查找在给定Assembly集中实现接口的所有类型:

var implementations = typeof(TypeInTargetAssembly).Assembly.GetTypes()
                          .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && t.IsClass);

If you have a type or an instance you can easily check if they support a specific interface.如果您有类型或实例,您可以轻松检查它们是否支持特定接口。

To test if an object implements a certain interface:测试一个对象是否实现了某个接口:

if(myObject is IMyInterface) {
  // object myObject implements IMyInterface
}

To test if a type implements a certain interface:测试一个类型是否实现了某个接口:

if(typeof(IMyInterface).IsAssignableFrom(typeof(MyType))) {
  // type MyType implements IMyInterface
}

If you got a generic object and want to do a cast as well as a check if the interface you cast to is implemented the code is:如果您有一个通用对象并想要进行转换以及检查您转换到的接口是否已实现,则代码为:

 var myCastedObject = myObject as IMyInterface;

    if(myCastedObject != null) {
      // object myObject implements IMyInterface
    }

Use Type.IsAssignableTo (as of .NET 5.0):使用Type.IsAssignableTo (从 .NET 5.0 开始):

typeof(MyType).IsAssignableTo(typeof(IMyInterface));

As stated in a couple of comments IsAssignableFrom may be considered confusing by being "backwards".正如几条评论中所述,IsAssignableFrom 可能被认为是“落后”的混淆。

Anyone searching for this might find the following extension method useful:任何搜索此内容的人都可能会发现以下扩展方法很有用:

public static class TypeExtensions
{
    public static bool ImplementsInterface(this Type type, Type @interface)
    {
        if (type == null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        if (@interface == null)
        {
            throw new ArgumentNullException(nameof(@interface));
        }

        var interfaces = type.GetInterfaces();
        if (@interface.IsGenericTypeDefinition)
        {
            foreach (var item in interfaces)
            {
                if (item.IsConstructedGenericType && item.GetGenericTypeDefinition() == @interface)
                {
                    return true;
                }
            }
        }
        else
        {
            foreach (var item in interfaces)
            {
                if (item == @interface)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

xunit tests: xunit 测试:

public class TypeExtensionTests
{
    [Theory]
    [InlineData(typeof(string), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<int>), true)]
    [InlineData(typeof(List<int>), typeof(IList<string>), false)]
    public void ValidateTypeImplementsInterface(Type type, Type @interface, bool expect)
    {
        var output = type.ImplementsInterface(@interface);
        Assert.Equal(expect, output);
    }
}

IsAssignableFrom现在移到TypeInfo

typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());

Note that if you have a generic interface IMyInterface<T> then this will always return false :请注意,如果您有一个通用接口IMyInterface<T>那么这将始终返回false

  typeof(IMyInterface<>).IsAssignableFrom(typeof(MyType)) /* ALWAYS FALSE */

This doesn't work either:这也不起作用:

  typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface<>))  /* ALWAYS FALSE */

However, if MyType implements IMyInterface<MyType> this works and returns true :但是,如果MyType实现了IMyInterface<MyType>这将起作用并返回true

  typeof(IMyInterface<MyType>).IsAssignableFrom(typeof(MyType))

However, you likely will not know the type parameter T at runtime .但是,您可能不会在运行时知道类型参数T A somewhat hacky solution is:一个有点hacky的解决方案是:

  typeof(MyType).GetInterfaces()
                .Any(x=>x.Name == typeof(IMyInterface<>).Name)

Jeff's solution is a bit less hacky:杰夫的解决方案不那么笨拙:

  typeof(MyType).GetInterfaces()
         .Any(i => i.IsGenericType 
             && i.GetGenericTypeDefinition() == typeof(IMyInterface<>));

Here's a extension method on Type that works for any case:这是一个适用于任何情况的Type扩展方法:

public static class TypeExtensions
{
    public static bool IsImplementing(this Type type, Type someInterface)
    {
        return type.GetInterfaces()
             .Any(i => i == someInterface 
                 || i.IsGenericType 
                    && i.GetGenericTypeDefinition() == someInterface);
    }
}

(Note that the above uses linq, which is probably slower than a loop.) (注意上面使用的是 linq,它可能比循环慢。)

You can then do:然后你可以这样做:

   typeof(MyType).IsImplementing(IMyInterface<>)

A correct answer is正确答案是

typeof(MyType).GetInterface(nameof(IMyInterface)) != null;

However,然而,

typeof(MyType).IsAssignableFrom(typeof(IMyInterface));

might return a wrong result, as the following code shows with string and IConvertible:可能会返回错误的结果,如以下代码显示的字符串和 IConvertible:

    static void TestIConvertible()
    {
        string test = "test";
        Type stringType = typeof(string); // or test.GetType();

        bool isConvertibleDirect = test is IConvertible;
        bool isConvertibleTypeAssignable = stringType.IsAssignableFrom(typeof(IConvertible));
        bool isConvertibleHasInterface = stringType.GetInterface(nameof(IConvertible)) != null;

        Console.WriteLine($"isConvertibleDirect: {isConvertibleDirect}");
        Console.WriteLine($"isConvertibleTypeAssignable: {isConvertibleTypeAssignable}");
        Console.WriteLine($"isConvertibleHasInterface: {isConvertibleHasInterface}");
    }

Results:结果:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

If you don't need to use reflection and you have an object, you can use this:如果你不需要使用反射并且你有一个对象,你可以使用这个:

if(myObject is IMyInterface )
{
 // it's implementing IMyInterface
}

what about怎么样

if(MyType as IMyInterface != null)

? ?

怎么样

typeof(IWhatever).GetTypeInfo().IsInterface

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM