簡體   English   中英

判斷object是否派生自集合類型

[英]Determine if object derives from collection type

我想確定泛型 object 類型 ("T") 方法類型參數是否為集合類型。 我通常會將 T 作為 Generic.List 發送,但它可以是任何集合類型,因為它在助手 function 中使用。

我是否最好測試它是否實現了 IEnumerable<T>?

如果是這樣,代碼會是什么樣子?

更新 14:17 GMT+10 可能在此處擴展解決方案(但僅適用於 List<T> 而不是 IEnumerable<T>,如果 List 派生,它應該在什么時候?)

T currentObj;    
// works if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(List<>)
// does not work if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(IEnumerable<>)

這將是最簡單的檢查。

if(Obj is ICollection)
{
    //Derived from ICollection
}
else
{
    //Not Derived from ICollection
}

您可以將 Type.GetInterface() 與損壞的名稱一起使用。

private bool IsTAnEnumerable<T>(T x)
{
    return null != typeof(T).GetInterface("IEnumerable`1");
}

為了在運行時獲得 T 的實際類型,您可以使用 typeof(T) 表達式。 從那里開始,普通的類型比較運算符會起作用

bool isEnumerable = typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));

完整代碼示例:

static bool Foo<T>()
{
  return typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));
}

Foo<List<T>>();  // true
Foo<int>(); // false

我個人傾向於使用我自己編寫的一種方法,稱為TryGetInterfaceGenericParameters ,我在下面發布了它。 以下是如何在您的情況下使用它:

使用示例

object currentObj = ...;  // get the object
Type[] typeArguments;
if (currentObj.GetType().TryGetInterfaceGenericParameters(typeof(IEnumerable<>), out typeArguments))
{
    var innerType = typeArguments[0];
    // currentObj implements IEnumerable<innerType>
}
else
{
    // The type does not implement IEnumerable<T> for any T
}

重要的是要注意,您傳入typeof(IEnumerable<>)而不是typeof(IEnumerable) (這是一種完全不同的類型),也不是typeof(IEnumerable<T>)任何T (如果您已經知道T ,你不需要這個方法)。 當然,這適用於任何通用接口,例如您也可以使用typeof(IDictionary<,>) (但不能使用typeof(IDictionary) )。

方法來源

/// <summary>
///     Determines whether the current type is or implements the specified generic interface, and determines that
///     interface's generic type parameters.</summary>
/// <param name="type">
///     The current type.</param>
/// <param name="interface">
///     A generic type definition for an interface, e.g. typeof(ICollection&lt;&gt;) or typeof(IDictionary&lt;,&gt;).</param>
/// <param name="typeParameters">
///     Will receive an array containing the generic type parameters of the interface.</param>
/// <returns>
///     True if the current type is or implements the specified generic interface.</returns>
public static bool TryGetInterfaceGenericParameters(this Type type, Type @interface, out Type[] typeParameters)
{
    typeParameters = null;

    if (type.IsGenericType && type.GetGenericTypeDefinition() == @interface)
    {
        typeParameters = type.GetGenericArguments();
        return true;
    }

    var implements = type.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == @interface, null).FirstOrDefault();
    if (implements == null)
        return false;

    typeParameters = implements.GetGenericArguments();
    return true;
}

我會測試IEnumerable ,因為集合類型只能實現IEnumerable ,它不必實現IEnumerable<T>

這還取決於:您對集合類型是什么意思? 您可以在不實現任何這些接口的情況下擁有一個集合。

另外,請記住,僅僅因為您使用的是泛型,不要忘記其他基本技術,在這種情況下,例如重載。 我懷疑你正在計划這樣的事情:

void SomeFunc<T>(T t)
{
    if (IsCollectionCase(t))
       DoSomethingForCollections()
    else
       DoSOmethingElse();
}

這會更好地處理為:

void SomeFunc(IEnumerable t)
{
       DoSomethingForCollections()
}
void SomeFunc<T>(T t)
{
       DoSomethingElse()
}

雖然我不能確定最初發布者的意圖是什么,但對轉換到 IEnumerable 進行測試的效果有幾種回應。 這很好,但每個人都應該知道字符串實例通過了這個測試,這可能不是原作者想要的。 我知道當我去尋找答案並找到這篇文章時,我當然沒有:

string testString = "Test";
Console.WriteLine(testString as IEnumerable != null);  // returns true

我正在嘗試編寫一個使用反射來完成某些任務的自定義序列化程序。 作為任務的一部分,我需要確定屬性值是集合/數組/項目列表還是單個屬性值。 特別令人討厭的是,幾個 Linq 表達式實際上會產生一個可枚舉類型的值,但 GetType().IsArray 為這些返回 false,將它們轉換為 ICollection 也返回 null,但將它們轉換為 IEnumerable 返回非空值。

所以……目前,我仍在尋找適用於所有情況的解決方案。

為了簡單和代碼共享,我通常使用這種擴展方法:

public static bool IsGenericList(this object obj)
{
    return IsGenericList(obj.GetType());
}

public static bool IsGenericList(this Type type)
{
    if (type == null)
    {
        throw new ArgumentNullException("type");
    }

    foreach (Type @interface in type.GetInterfaces())
    {
        if (@interface.IsGenericType)
        {
            if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                // if needed, you can also return the type used as generic argument
                return true;
            }
        }
    }

    return (type.GetInterface("IEnumerable") != null);
}

我喜歡泛型。 在此方法中, T必須有一個公共且無參數的構造函數,這意味着您不能將IList<object>用於T 您必須使用List<object>

public static T IsEnumerable<T>() where T : new() {
    if (new T() is IEnumerable) {
}

我在嘗試將任何對象序列化為 JSON 格式時遇到了同樣的問題。 這是我最終使用的:

Type typ = value.GetType();

// Check for array type
if(typeof(IEnumerable).IsAssignableFrom(typ) || typeof(IEnumerable<>).IsAssignableFrom(typ))
{ 
    List<object> list = ((IEnumerable)value).Cast<object>().ToList();
    //Serialize as an array with each item in the list...
}
else
{
    //Serialize as object or value type...
}

如果您想對任何列表/集合/IEnumerable 進行檢查並使其為真,但對於字符串類型為假,則

   private static bool IsIEnumerable(Type requestType)
   {
      var isIEnumerable = typeof(IEnumerable).IsAssignableFrom(requestType);
      var notString = !typeof(string).IsAssignableFrom(requestType);
      return isIEnumerable && notString;
   }

對於任何類型的 ICollection(例如 List 或 HashSet):

internal static bool IsCollection(this Type type) => type.GetGenericArguments().Length > 0 && (typeof(ICollection<>).MakeGenericType(type.GetGenericArguments()[0])).IsAssignableFrom(type);

出口工廠

[英]ExportFactory<T,TMetaData - Get type of class which derives from T

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM