형식이 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이므로 코드에 예기치 않은 결과가 발생할 수 있습니다.
Chris Kemp

29
주의를 기울이지 않고 IsAssignableFrom거꾸로 논쟁을하는 것은 쉬운 일이었습니다 . 나는 GetInterfaces지금 갈 것이다 : p
Benjamin

12
IsAssignableFrom(t1)변형은 GetInterfaces().Contains(t2)내 코드에 비해 약 3 배 빠릅니다 .
Pierre Arnaud

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반영 비용을 포함하지 않는 것입니다. 따라서 잘못된 것은 아니지만 이상적인 방법은 아닙니다.
James J. Regan IV

1
@ 제임스-동의합니다. Resharper조차도 같은 제안을합니다.
Angshuman Agarwal

@ JamesJ.ReganIV 당신은 답변으로, 나는 당신의 의견을 거의 놓쳤다
reggaeguitar

@ reggaeguitar, 감사하지만 의견은 원래 질문에 대답하지 않습니다. 질문은 리플렉션 솔루션을 요구합니다.이 답변의 첫 번째 사례에서 객체 리플렉션 인스턴스가 이상적인 솔루션이 아니라고 말하고 있습니다.
James J. Regan 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. IsAssignableFrom이 아닌 GetInterfaces를 사용합니다. IsAssignableFrom이 결국 여러 검사 후 GetInterfaces를 호출 한 후 더 빠릅니다.
  2. 로컬 배열을 반복하므로 바운드 검사가 없습니다.
  3. Type에 대해 정의 된 == 연산자를 사용하므로 Equals 메소드보다 안전합니다 (Contains 호출이 결국에 사용됨).

10
내용 +1, 나는 parens와 이집트 괄호 주위의 공간이 싫어. 또한 전체 메소드를 다음과 같이 작성할 수 있습니다. return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar

1
Type.IsAssignableFrom () internaly는 코드와 똑같이 작동합니다.
devi

1
또한 LINQ를 사용하지 않는 type.GetInterfaces (). Contains (ifaceType)을 입력하지 않는 이유는 무엇입니까?

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를 리턴 할 수 있습니다 . 인터페이스 유형 대신 클래스 유형으로 사용하면 type-parameter가 인터페이스가 아닌 경우 예외가 발생합니다. 파생 클래스 '구현물'이 부모라고 주장 할 수 있지만 ...
Sindri Jóelsson

7

최적의 성능을 위해 Jeff의 답변 수정 (Pierre Arnaud의 성능 테스트 덕분에) :

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

다른 사람이 이미 언급 한 것처럼 : Benjamin Apr 10 '13 at 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
확장 메소드에 구현을 직접 넣지 않은 이유가 있습니까? 나는 이것이 두 가지 방법으로 그것을 부를 수 있다고 확신하지만 왜 그렇게해야합니까?
Mark A. Donohoe 2012 년

@MarqueIV 거의 2 년 늦게 당신에게 돌아와서 죄송합니다. 잘 반복되는 코드를 피하기 위해 확장 방법으로 도우미 메서드를 래핑하는 것이 오래 된 나쁜 습관이라고 생각합니다. :)
Kerry Perret

1
@MarqueIV, 수행 플러스 별칭을 사용하지 않는 내 다른 나쁜 습관을 변화 즉 Boolean=> bool(내가 어렸을 때 코딩의 일부 엄격한 "공상"규칙을 가지고하는 데 사용 왜하지 않습니다).
Kerry Perret

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

이건 어떤가요

if(MyType as IMyInterface != null)

?


4
인스턴스가있을 때 이것은 분명합니다. 반사에서 유형이있을 때 유용하지 않음
edc65

0

이건 어떤가요

typeof(IWhatever).GetTypeInfo().IsInterface

0

정답은

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

하나,

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

다음 코드가 string 및 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. 벤자민과 Ehouarn이 경고 한 것처럼.
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런타임에 type 매개 변수 를 알지 못할 수 있습니다. 다소 해키 솔루션은 다음과 같습니다.

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

Jeff의 솔루션은 약간 덜 해킹 적입니다.

  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<>)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.