Cách xác định xem một loại có thực hiện một loại giao diện chung cụ thể không


226

Giả sử các định nghĩa loại sau:

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

Làm cách nào để tìm hiểu xem loại này có Foothực hiện giao diện chung hay không IBar<T>khi chỉ có loại mangled?

Câu trả lời:


387

Bằng cách sử dụng câu trả lời từ TcKs, nó cũng có thể được thực hiện với truy vấn LINQ sau:

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

1
Đây là một giải pháp rất thanh lịch! Những cái khác tôi đã thấy trên SO sử dụng các vòng lặp foreach hoặc các truy vấn LINQ dài hơn. Hãy ghi nhớ rằng để sử dụng cái này, bạn cần phải có .NET framework 3.5.
Daniel T.

7
Tôi khuyên bạn nên thực hiện phương thức mở rộng này thành một bit.ly/ccza8B - sẽ dọn sạch phần này khá độc đáo!
Brad Heller

1
Tùy thuộc vào nhu cầu của bạn, bạn có thể thấy bạn cần tái diễn trên các giao diện được trả về.
Sebastian Tốt

4
Tôi muốn nói rằng điều này nên được triển khai trong .net tốt hơn nhiều ... như là một lõi ... như thành viên. Các ứng dụng (IBar) hoặc CustomType.Implements (IBar), hoặc thậm chí tốt hơn, sử dụng từ khóa "là" .. .. Tôi đang khám phá c # và bây giờ tôi hơi thất vọng về .net ngay bây giờ ...
Sofija

2
bổ sung nhỏ: nếu IBar có nhiều loại chung chung, bạn cần chỉ ra điều này như sau: typeof(IBar<,,,>)với dấu phẩy đóng vai trò giữ chỗ
Rob Von Nesselrode

33

Bạn phải đi qua cây thừa kế và tìm tất cả các giao diện cho mỗi lớp trong cây và so sánh typeof(IBar<>)với kết quả của việc gọi Type.GetGenericTypeDefinition nếu giao diện là chung. Đó là tất cả một chút đau đớn, chắc chắn.

Xem câu trả lời nàynhững câu hỏi này để biết thêm thông tin và mã.


tại sao không chuyển sang IBar <someClass> và kiểm tra null? (Ý tôi là đúc với 'như' tất nhiên)
Pablo Retyk

5
T không xác định và không thể chuyển sang một loại cụ thể.
sduplooy

@sduplooy: có lẽ tôi đang thiếu thứ gì đó làm sao T không biết? nó sẽ biên dịch lớp công khai Foo: IFoo <T> {}
Pablo Retyk

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
Vì typeof (Foo) trả về đối tượng System.Type (mô tả Foo), nên lệnh gọi GetType () sẽ luôn trả về kiểu cho System.Type. Bạn nên đổi thành typeof (Foo) .GetInterfaces ()
Michael Meadows

9

Là một phần mở rộng phương thức trợ giúp

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

Ví dụ sử dụng:

var testObject = new Dictionary<int, object>();
result = testObject.GetType().Implements(typeof(IDictionary<int, object>)); // true!

2
"IsAssignableFrom" chính xác là những gì tôi đang tìm kiếm - cảm ơn
Jesper

22
Điều này không hoạt động đối với yêu cầu của người hỏi về việc không biết tham số loại chung. Từ ví dụ của bạn testObject.GetType (). Implements (typeof (IDadata <,>)); sẽ trả lại sai.
ctusch

@ctusch thì, giải pháp nào cho việc đó?
Tohid

5

Tôi đang sử dụng một phiên bản đơn giản hơn của phương thức tiện ích mở rộng @GenericProgrammer:

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

Sử dụng:

    if (!featureType.Implements<IFeature>())
        throw new InvalidCastException();

5
Nó vẫn không hoạt động theo yêu cầu ban đầu của câu hỏi dành cho giao diện chung.
nathanchere

4

Bạn phải kiểm tra đối với loại được xây dựng của giao diện chung.

Bạn sẽ phải làm một cái gì đó như thế này:

foo is IBar<String>

bởi vì IBar<String>đại diện cho loại xây dựng. Lý do bạn phải làm điều này là vì nếu Tkhông được xác định trong séc của bạn, trình biên dịch không biết bạn có ý IBar<Int32>hay không IBar<SomethingElse>.


4

Để giải quyết những hệ thống kiểu hoàn toàn, tôi nghĩ rằng bạn cần phải xử lý đệ quy, ví dụ IList<T>: ICollection<T>: IEnumerable<T>, mà không có nó bạn sẽ không biết rằng IList<int>cuối cùng cụ 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

Trước hết public class Foo : IFoo<T> {}không biên dịch vì bạn cần chỉ định một lớp thay vì T, nhưng giả sử bạn làm một cái gì đó nhưpublic class Foo : IFoo<SomeClass> {}

sau đó nếu bạn làm

Foo f = new Foo();
IBar<SomeClass> b = f as IBar<SomeClass>;

if(b != null)  //derives from IBar<>
    Blabla();

2

Trong trường hợp bạn muốn một phương thức mở rộng hỗ trợ các loại cơ sở chung cũng như giao diện, tôi đã mở rộng câu trả lời của sduploo:

    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

Phương pháp kiểm tra xem kiểu kế thừa hay thực hiện kiểu chung:

   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

Hãy thử phần mở rộng sau đây.

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

Để kiểm tra nó. tạo nên

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

và phương pháp thử

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

Không nên có bất cứ điều gì sai sau đây:

bool implementsGeneric = (anObject.Implements("IBar`1") != null);

Để có thêm tín dụng, bạn có thể bắt gặp AmbiguptMatchException nếu bạn muốn cung cấp một tham số loại chung cụ thể với truy vấn IBar của mình.


Chà, tốt hơn hết là tránh sử dụng chuỗi ký tự khi có thể. Cách tiếp cận này sẽ khiến việc tái cấu trúc ứng dụng trở nên khó khăn hơn, vì đổi tên giao diện IBar sẽ không thay đổi chuỗi ký tự và lỗi chỉ có thể được phát hiện khi chạy.
andyroschy

Nhiều như tôi thường đồng ý với nhận xét ở trên về việc sử dụng 'chuỗi ma thuật', v.v., đây vẫn là cách tiếp cận tốt nhất tôi tìm thấy. Cũng đủ gần - thử nghiệm cho PropertyType. Hãy cân bằng "IWhthing`1".
nathanchere

Tại sao không phải điều này? bool implementsGeneric = (anObject.Implements(typeof(IBar<>).Name) != null);
Maxime Gélinas
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.