2010-02-23 28 views
5

Tôi có một phương pháp kiểm tra nếu một loại là chung chung và sau đó kiểm tra nếu GenericTypeDefinition là IEnumerable<>.Làm thế nào tôi có thể nhìn thấy nếu GenericTypeDefinition triển khai IEnumerable <>

static Type GetEnumerableType(Type type) 
{ 
    if(type.IsGenericType) { 
     var genericTypeDefinition = type.GetGenericTypeDefinition(); 
     if (genericTypeDefinition == typeof(IEnumerable<>)) { 
      return type.GetGenericArguments()[0]; 
     } 
    } 
    return null; 
} 

Hoạt động như một nét duyên dáng nếu nó là IEnumerable. Nếu GenericTypeDefinition là IList<> hoặc List<> thì nó không hoạt động. Tôi đã thử ..

typeof(IEnumerable<>).IsAssignableFrom(genericTypeDefinition) 

..không thành công. Tất nhiên phải có một cách tốt hơn sau đó chuỗi khác tuyên bố?

+1

Tôi nghĩ rằng giải pháp Stan R. có lẽ là khoảng những gì bạn đang tìm kiếm, nhưng vấn đề của bạn là underspecified . Bạn chỉ quan tâm đến các loại chung chung hay bạn cũng có thể quan tâm đến các loại không chung chung (ví dụ: một loại không chung chung đang triển khai 'IEnumerable ')? – kvb

+0

Tại buổi lễ, tôi chỉ quan tâm đến các loại chung chung, nhưng bạn có một điểm tốt. –

Trả lời

10

Bạn có thể sử dụng GetInterfaces để kiểm tra xem một loại thực IEnumerable<> như vậy

Type type = new List<string>().GetType(); 

if (type.IsGenericType) 
{ 
    var genericTypeDefinition = type.GetGenericTypeDefinition(); 

    if (genericTypeDefinition.GetInterfaces() 
       .Any(t => t.IsGenericType && 
          t.GetGenericTypeDefinition() == typeof(IEnumerable<>))) 
    { 
     return type.GetGenericArguments()[0]; 
    } 
} 
+0

Hoạt động hoàn hảo. –

+1

Phương thức này sẽ trả về false cho 'MyList: List ' hoặc 'yield return @ string' kể từ' MyList' và máy trạng thái được tạo ra bởi yield không phải là generic classes và 'IsGenericType' là false –

6

Tôi đã kết thúc việc tìm hiểu sâu hơn về xử lý đối tượng chung và thấy cách này phức tạp hơn bất kỳ giả định ban đầu nào. Đây là phương pháp bây giờ tôi đang sử dụng:

/// <summary>Check whether the specified type is enumerable.</summary> 
/// <param name="type">The type.</param> 
/// <param name="underlyingType">IEnumerable{int} would be int</param> 
/// <param name="excludeString"> 
/// [OPTIONAL] if set to <c>true</c> [exclude string]. Strings are enumerable as char[] 
/// this is likely not something you want. Default is true (string will return false) 
/// </param> 
/// <returns><c>true</c> supplied type is enumerable otherwise <c>false</c></returns> 
public static bool IsEnumerable(this Type type, out Type underlyingType, 
           bool excludeString = true) 
{ 
    underlyingType = null; 

    if (type.IsEnum || type.IsPrimitive || type.IsValueType) return false; 

    if (excludeString && type == typeof(string)) return false; 

    if (type.IsGenericType) 
    { 
     if (type.IsTypeDefinitionEnumerable() || 
      type.GetInterfaces() 
       .Any(t => t.IsSelfEnumerable() || t.IsTypeDefinitionEnumerable())) 
     { 
      underlyingType = type.GetGenericArguments()[0]; 
      return true; 
     } 
    } 
    //direct implementations of IEnumerable<T>, inheritance from List<T> etc 
    var enumerableOrNull = type.GetInterfaces() 
           .FirstOrDefault(t => t.IsTypeDefinitionEnumerable()); 
    if (enumerableOrNull == null) return false; 

    underlyingType = enumerableOrNull.GetGenericArguments()[0]; 
    return true; 
} 

//

private static bool IsSelfEnumerable(this Type type) 
{ 
    bool isDirectly = type == typeof(IEnumerable<>); 
    return isDirectly; 
} 

private static bool IsTypeDefinitionEnumerable(this Type type) 
{ 
    bool isViaInterfaces = type.IsGenericType && 
          type.GetGenericTypeDefinition().IsSelfEnumerable(); 
    return isViaInterfaces; 
} 

Giải pháp này được thử nghiệm:

Install-Package NUnit -version 2.6.4

Gói cài đặt nên

[Test] 
public void List_is_enumerable() 
{ 
    var sut = new List<int>(); 

    Type underlyingType; 
    var result = sut.IsEnumerable(out underlyingType); 

    result.ShouldBeTrue(); 
    underlyingType.ShouldBe(typeof(int)); 
} 

//

[Test] 
public void Yield_return_is_enumerable() 
{ 
    var sut = Yielded(); 

    Type underlyingType; 
    var result = sut.IsEnumerable(out underlyingType); 

    result.ShouldBeTrue(); 
    underlyingType.ShouldBe(typeof(int)); 
} 

private IEnumerable<int> Yielded() 
{ 
    for (int i = 0; i < 3; i++) 
    { 
     yield return i; 
    } 
} 

//

[Test] 
public void int_is_not_an_enumerable() 
{ 
    var sut = 5; 

    Type underlyingType; 
    var result = sut.IsEnumerable(out underlyingType); 

    result.ShouldBe(false); 
    underlyingType.ShouldBeNull(); 
} 

[Test] 
public void object_is_not_an_enumerable() 
{ 
    var sut = new { foo = 1}; 

    Type underlyingType; 
    var result = sut.IsEnumerable(out underlyingType); 

    result.ShouldBe(false); 
    underlyingType.ShouldBeNull(); 
} 

giữ cho hậu thế. Điều này không trả lời câu hỏi ban đầu nhưng rõ ràng là hữu ích cho các thành viên ở đây.

public static bool IsA<T>(this Type type) 
{ 
    return typeof (T).IsAssignableFrom(type); 
} 
+0

+1: Đối với định nghĩa IsA - đó là một tiện ích. –

+0

Nó không giải quyết được vấn đề. Và, vâng, tôi cũng thường nhầm lẫn phương pháp IsAssignableFrom, nhưng tôi không nghĩ đó là trường hợp ở đây. –

+1

Mã chỉnh sửa của bạn với 'if (type.IsA >) {' sẽ không biên dịch vì nó muốn đối số kiểu. –

Các vấn đề liên quan