Cách xác định xem một loại có thực hiện giao diện với phản chiếu C # không


562

Liệu sự phản chiếu trong C#cung cấp một cách để xác định nếu một số System.Typeloại mô hình nhất định một số giao diện?

public interface IMyInterface {}

public class MyType : IMyInterface {}

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

Câu trả lời:


969

Bạn có một vài lựa chọn:

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

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

Đối với một giao diện chung, nó hơi khác một chút.

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

68
Hãy nhớ rằng typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) cũng đúng, điều này có thể có kết quả không mong muốn trên mã của bạn.
Chris Kemp

29
Nó chắc chắn là dễ dàng để không chú ý và nhận được các đối số cho IsAssignableFromngược. Tôi sẽ đi với GetInterfacesbây giờ: p
Benjamin

12
Các IsAssignableFrom(t1)biến thể là về 3x nhanh hơn so với GetInterfaces().Contains(t2)đối tác trong mã của tôi.
Pierre Arnaud

24
@PierreArnaud: IsAssignableFrom cuối cùng cũng gọi GetInterfaces, vì vậy có lẽ bài kiểm tra của bạn đã kiểm tra GetInterfaces trước và IsAssignable sau. Đó là bởi vì GetInterfaces lưu trữ kết quả của nó, vì vậy lần gọi đầu tiên có chi phí cao hơn
Panos Theof

17
Một thay đổi nhỏ đối với câu trả lời của @ Kosta. Với C # 6, chúng ta có thể thực hiện typeof(MyType).GetInterface(nameof(IMyInterface)) != nullđể tái cấu trúc và an toàn tốt hơn.
aholmes


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

hoặc là

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

34
Nếu bạn đã có một thể hiện của lớp, cách tiếp cận tốt hơn nhiều chỉ đơn giản someclass is IMyInterfacelà điều đó không liên quan đến chi phí phản ánh. Vì vậy, trong khi không sai, nó không phải là một cách lý tưởng để làm điều đó.
James J. Regan IV

1
@James - Đồng ý. Ngay cả Resharper cũng đưa ra gợi ý tương tự.
Angshuman Agarwal

@ JamesJ.ReganIV bạn nên đăng nó như một câu trả lời, tôi gần như đã bỏ lỡ bình luận của bạn
reggaeg Ức

@reggaeg Ức, cảm ơn, nhưng bình luận không trả lời câu hỏi ban đầu. Câu hỏi yêu cầu giải pháp Reflection, tôi chỉ nói trong trường hợp đầu tiên của câu trả lời này khi bạn có một ví dụ về sự phản chiếu đối tượng không phải là giải pháp lý tưởng.
James J. Regan IV

1
@ JamesJ.ReganIV Trên thực tế, iskiểm tra theo cả hai hướng của hệ thống phân cấp thừa kế trong khi IsAssignableFromchỉ kiểm tra lên trên. Ngoài ra, nếu bạn có một thể hiện của một đối tượng, bạn nên gọi IsInstanceOfType(cái này cũng chỉ nhìn lên trên).
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;
}

Tôi nghĩ rằng đây là bản phát hành chính xác, vì ba lý do:

  1. Nó sử dụng GetInterfaces chứ không phải IsAssignableFrom, nó nhanh hơn kể từ IsAssignableFrom sau khi một số kiểm tra gọi GetInterfaces.
  2. Nó lặp lại trên mảng cục bộ, do đó sẽ không có kiểm tra giới hạn.
  3. Nó sử dụng toán tử == được xác định cho Loại, vì vậy có lẽ an toàn hơn phương thức Equals (mà cuộc gọi Chứa, cuối cùng sẽ sử dụng).

10
+1 cho nội dung, tôi ghét không gian xung quanh parens và niềng răng Ai Cập. Ngoài ra, toàn bộ phương thức có thể được viết là: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeg Ức

1
Type.IsAssignableFrom () internaly hoạt động chính xác như mã của bạn
devi

1
Ngoài ra, tại sao không gõ.GetInterfaces (). Chứa (ifaceType) không sử dụng LINQ.

9

Tôi vừa làm:

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

Tôi ước tôi có thể nói where I : interface, nhưng interfacekhông phải là một tùy chọn ràng buộc tham số chung.classlà gần như nó được.

Sử dụng:

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

Tôi chỉ nói Implementsvì điều đó trực quan hơn. Tôi luôn luôn bị IsAssignableFromlật.


Bạn có thể làm return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);để trả về false trên bất kỳ cách sử dụng 'không chính xác' nào của phương thức, nghĩa là; sử dụng nó với một loại lớp thay vì một loại giao diện, thay vào đó sẽ ném một ngoại lệ nếu tham số loại không phải là một giao diện. Mặc dù bạn có thể lập luận rằng một lớp dẫn xuất 'thực hiện' nó là cha mẹ ...
Sindri Jóelsson

7

Sửa đổi câu trả lời của Jeff để có hiệu suất tối ưu (nhờ kiểm tra hiệu suất của Pierre Arnaud):

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

Để tìm tất cả các loại thực hiện một giao diện trong một cho trước Assembly:

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

7

Như một người khác đã đề cập: Benjamin 10 tháng 4 '13 lúc 22:21 "

Thật dễ dàng để không chú ý và nhận được các đối số cho IsAssignableFrom ngược. Tôi sẽ đi với GetInterfaces ngay bây giờ: p -

Vâng, một cách khác là chỉ tạo ra một phương pháp mở rộng ngắn, đáp ứng, ở một mức độ nào đó, cách suy nghĩ "thông thường nhất" (và đồng ý đây là một lựa chọn cá nhân rất ít để làm cho nó hơi "tự nhiên" hơn dựa trên sở thích của một người ):

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

Và tại sao không nói chung chung hơn một chút (cũng không chắc nó có thực sự thú vị không, tôi cho rằng tôi chỉ đang vượt qua một nhúm đường 'cú pháp' khác):

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

Tôi nghĩ rằng nó có thể tự nhiên hơn nhiều theo cách đó, nhưng một lần nữa chỉ là vấn đề của những ý kiến ​​rất cá nhân:

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

4
Có một lý do nào khiến bạn không đặt việc triển khai trực tiếp vào phương thức mở rộng? Tôi có nghĩa là chắc chắn điều này cho phép bạn gọi nó theo cả hai cách, nhưng tại sao bạn cần phải làm điều đó?
Đánh dấu A. Donohoe

@MarqueIV xin lỗi vì đã quay lại với bạn muộn gần 2 năm, tôi đoán đó là một thói quen xấu cũ sau đó để bọc phương thức trợ giúp trong phương thức mở rộng để tránh lặp lại mã, sẽ chỉnh sửa câu trả lời của tôi :)
Kerry Perret

1
@MarqueIV đã thực hiện cộng với thay đổi thói quen xấu khác của tôi là không sử dụng bí danh, tức là Boolean=> bool(Tôi không biết tại sao tôi từng có một số quy tắc mã hóa "ưa thích" nghiêm ngặt khi tôi còn trẻ).
Kerry Perret

3

Nếu bạn có một loại hoặc một thể hiện, bạn có thể dễ dàng kiểm tra xem chúng có hỗ trợ một giao diện cụ thể không.

Để kiểm tra nếu một đối tượng thực hiện một giao diện nhất định:

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

Để kiểm tra nếu một loại thực hiện một giao diện nhất định:

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

Nếu bạn có một đối tượng chung và muốn thực hiện phân vai cũng như kiểm tra xem giao diện bạn truyền tới có được thực thi mã không:

 var myCastedObject = myObject as IMyInterface;

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

2

IsAssignableFromhiện được chuyển đến TypeInfo:

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

1

Bất cứ ai tìm kiếm điều này có thể thấy phương pháp mở rộng sau đây hữu ích:

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

xét nghiệm 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

Thế còn

if(MyType as IMyInterface != null)

?


4
Điều này là hiển nhiên khi tôi có một ví dụ. Không hữu ích khi tôi có Loại từ phản chiếu
edc65

0

Thế còn

typeof(IWhatever).GetTypeInfo().IsInterface

0

Một câu trả lời đúng là

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

Tuy nhiên,

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

có thể trả về kết quả sai, vì đoạn mã sau hiển thị với chuỗi và 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}");
    }

Các kết quả:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

4
Như bạn có thể thấy trong câu trả lời được chấp nhận, bạn đã thay thế các loại trong cách sử dụng IsAssignableFrom. Giống như Benjamin và Ehouarn cảnh báo.
VV5198722

0

Lưu ý rằng nếu bạn có một giao diện chung IMyInterface<T>thì điều này sẽ luôn trả về false:

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

Điều này cũng không hoạt động:

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

Tuy nhiên, nếu MyTypethực hiện IMyInterface<MyType>điều này hoạt động và trả về true:

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

Tuy nhiên, bạn có thể sẽ không biết tham số loại Tkhi chạy . Một giải pháp hơi khó là:

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

Giải pháp của Jeff là một chút ít hacky:

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

Đây là một phương pháp mở rộng trên Typeđó hoạt động cho mọi trường hợp:

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

(Lưu ý rằng ở trên sử dụng linq, có thể chậm hơn vòng lặp.)

Sau đó bạn có thể làm:

   typeof(MyType).IsImplementing(IMyInterface<>)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.