Как определить, реализует ли тип интерфейс с отражением C #


562

Есть ли отражение в C#предложении таким образом , чтобы определить , если некоторые заданные System.Typeмодели типа некоторого интерфейса?

public interface IMyInterface {}

public class MyType : IMyInterface {}

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

Ответы:


969

У вас есть несколько вариантов:

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

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

Для общего интерфейса это немного по-другому.

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

68
Помните, что typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) также имеет значение true, что может привести к неожиданному результату для вашего кода.
Крис Кемп

29
Конечно, было легко не обращать внимания и приводить аргументы в IsAssignableFromобратном направлении. Я пойду с GetInterfacesсейчас: р
Бенджамин

12
IsAssignableFrom(t1)Вариант примерно в 3 раза быстрее , чем GetInterfaces().Contains(t2)аналог в моем коде.
Пьер Арно

24
@PierreArnaud: IsAssignableFrom в конечном итоге вызывает GetInterfaces, поэтому, вероятно, ваш тест сначала проверял GetInterfaces, а IsAssignable после. Это связано с тем, что GetInterfaces кэширует свои результаты, поэтому первый вызов стоит дороже
Panos Theof

17
Небольшое изменение в ответе @ Kosta. С C # 6 мы можем typeof(MyType).GetInterface(nameof(IMyInterface)) != nullулучшить безопасность типов и рефакторинг.
Aholmes


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

или

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

34
Если у вас уже есть экземпляр класса, гораздо лучший подход заключается someclass is IMyInterfaceв том, что он просто не требует затрат на рефлексию. Таким образом, хотя это не так, это не идеальный способ сделать это.
Джеймс Дж. Риган IV

1
@James - согласен Даже Решарпер дает то же самое предложение.
Ангшуман Агарвал

@ JamesJ.ReganIV, вы должны опубликовать это как ответ, я почти пропустил ваш комментарий
reggaeguitar

@reggaeguitar, спасибо, но комментарий не отвечает на первоначальный вопрос. Вопрос требует решения «Отражение», я просто говорю, что в первом случае этого ответа у вас есть экземпляр отражения объекта, который не является идеальным решением.
Джеймс Дж. Риган IV

1
@ JamesJ.ReganIV На самом деле, isпроверяет в обоих направлениях иерархии наследования, тогда как IsAssignableFromпроверяет только вверх. Также, если у вас есть экземпляр объекта, вы должны вызывать IsInstanceOfType(который также смотрит только вверх).
Sellorio

13
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;
}

Я думаю, что это правильный выпуск по трем причинам:

  1. Он использует GetInterfaces, а не IsAssignableFrom, он работает быстрее, поскольку IsAssignableFrom в конечном итоге после нескольких проверок вызывает GetInterfaces.
  2. Он перебирает локальный массив, поэтому проверки границ не будет.
  3. Он использует оператор ==, который определен для Type, поэтому, вероятно, безопаснее, чем метод Equals (который в конечном итоге будет использовать вызов Contains).

10
+1 за содержание, я ненавижу места вокруг паренов и египетских скобок, хотя. Также весь метод может быть записан как: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar

1
Type.IsAssignableFrom () internaly действует точно так же , как ваш код
деви

1
Также почему бы не type.GetInterfaces (). Содержит (ifaceType), который не использует LINQ.

9

Я только что сделал:

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

Я хотел бы сказать where I : interface, но interfaceэто не общий параметр ограничения параметра. classнастолько близко, насколько это возможно.

Применение:

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

Я просто сказал, Implementsпотому что это более интуитивно понятно. Я всегда IsAssignableFromшлепаюсь.


Вы можете сделать так, return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);чтобы возвращать false при любом «неправильном» использовании метода, то есть; используя его с типом класса вместо типа интерфейса, вы можете вызвать исключение, если параметр типа не является интерфейсом. Хотя вы можете утверждать, что производный класс «реализует» его родителя ...
Синдри Йоелссон

7

Изменение ответа Джеффа для достижения оптимальной производительности (благодаря тесту производительности Пьера Арно):

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

Чтобы найти все типы, которые реализуют интерфейс в данном Assembly:

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

7

Как уже упоминал кто-то другой: Бенджамин 10 апреля '13 в 22:21

Конечно, было легко не обратить внимание и получить аргументы для IsAssignableFrom в обратном направлении. Я пойду с GetInterfaces сейчас: p -

Что ж, другой путь - просто создать короткий метод расширения, который в некоторой степени соответствует «наиболее привычному» образу мышления (и согласился, что это очень маленький личный выбор, чтобы сделать его немного «более естественным» в зависимости от предпочтений). ):

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

И почему бы не пойти немного более обобщенно (ну, не уверен, действительно ли это так интересно, я полагаю, что я просто пропускаю очередную щепотку «синтаксического» сахара):

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));
    }
}

Я думаю, что это может быть гораздо более естественным, но, опять же, вопрос личного мнения:

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

4
Есть ли причина, по которой вы не просто поместили реализацию непосредственно в метод расширения? Я имею в виду, конечно, это позволяет вам называть это обоими способами, но зачем вам это нужно?
Марк А. Донохо

@MarqueIV извините, что вернулся к вам с опозданием почти на 2 года, я думаю, это была старая дурная привычка - использовать вспомогательный метод в методе расширения, чтобы избежать повторения кода, и отредактирую мой ответ :)
Керри Перре,

1
@MarqueIV сделал плюс изменил мою другую плохую привычку не использовать псевдоним, т.е. Boolean=> bool(я не знаю, почему у меня были строгие «причудливые» правила кодирования, когда я был моложе).
Керри Перре

3

Если у вас есть тип или экземпляр, вы можете легко проверить, поддерживают ли они определенный интерфейс.

Чтобы проверить, реализует ли объект определенный интерфейс:

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

Чтобы проверить, реализует ли тип определенный интерфейс:

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

Если вы получили универсальный объект и хотите выполнить приведение, а также проверить, реализован ли интерфейс, к которому вы приведете, код:

 var myCastedObject = myObject as IMyInterface;

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

2

IsAssignableFromсейчас перемещено в TypeInfo:

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

1

Любой, кто ищет это, может найти следующий метод расширения полезным:

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:

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);
    }
}



0

Правильный ответ

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

Однако,

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

может вернуть неправильный результат, как показано в следующем коде со строкой и 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}");
    }

Результаты:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

4
Как видно из принятого ответа, вы поменялись типами при использовании IsAssignableFrom. Так же, как предупреждают Бенджамин и Эуарн.
VV5198722

0

Обратите внимание, что если у вас есть универсальный интерфейс, IMyInterface<T>он всегда будет возвращать false:

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

Это тоже не работает:

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

Однако, если MyTypeреализует IMyInterface<MyType>это работает и возвращает true:

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

Однако вы, вероятно, не будете знать параметр типа Tво время выполнения . Несколько хакерское решение:

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

Решение Джеффа немного менее хакерское:

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

Вот метод расширения, 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);
    }
}

(Обратите внимание, что выше используется linq, который, вероятно, медленнее, чем цикл.)

Затем вы можете сделать:

   typeof(MyType).IsImplementing(IMyInterface<>)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.