[英]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<>) or typeof(IDictionary<,>).</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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.