我们说我有一个可能属于IEnumerable<T>
类型的对象。我想写一个方法,如果对象的类型为IEnumerable<T>
,非空,并且不为空,则返回true。
这是我到目前为止所得到的:
public bool IsNullOrEmpty(object obj)
{
if (obj != null)
{
if (obj is IEnumerable<object>)
{
return (obj as IEnumerable<object>).Any();
}
}
return false;
}
如果我传入类型为List<string>
的对象,则会起作用,但如果我传入类型为List<int>
的对象则不行。它失败是因为obj is IEnumerable<object>
返回false
。
知道我如何才能使所有通用IEnumerable
的工作成为可能吗?
答案 0 :(得分:10)
由于类型可能未知,您可以尝试检查IEnumerable
界面并在枚举器上使用MoveNext()
。
编辑:我更新了方法名称。由于原始问题代码检查集合中是否有项目,因此现在使用逻辑更有意义。
public bool IsNotNullOrEmpty(object enumerable)
{
if (enumerable != null)
{
if (enumerable is IEnumerable)
{
using(var enumerator = ((IEnumerable)enumerable).GetEnumerator())
return enumerator.MoveNext();
}
}
return false;
}
答案 1 :(得分:8)
System.Collections.Generic.IEnumerable<T>
继承自System.Collections.IEnumerable
- 因此,如果您可以检查非通用IEnumerable
,而不是通用IEnumerable<T>
,则可以转换为{ {1}}。
关于您的代码的一些注意事项:您首先使用IEnumerable
进行检查,然后使用is
进行投射。这通常是不必要的;如果演员失败,as
已经检查并返回as
。因此,更短的方式是:
null
请注意,您需要对Cast
进行额外调用,因为Any
需要通用var enumerable = obj as IEnumerable;
if (enumerable != null) {
return !enumerable.Cast<object>().Any();
}
。
答案 2 :(得分:2)
您可以尝试将其投射到IEnumerable
:
public static bool IsNullOrEmpty<T>(this T obj) where T : class
{
if (obj == null) return true;
IEnumerable seq = obj as IEnumerable;
if (seq != null) return !seq.Cast<object>().Any();
return false;
}
...
List<int> list = new List<int>();
bool nullOrEmpty = list.IsNullOrEmpty(); // true
顺便说一句,有趣的是它可以正确使用空字符串:
bool nullOrEmpty = "".IsNullOrEmpty(); // true
答案 3 :(得分:2)
您可以检查非通用IEnumerable
并检查是否为空。您可以添加一个检查以确保对象使用反射实现IEnumerable<T>
:
public static bool IsNullOrEmpty(object obj)
{
var e = obj as System.Collections.IEnumerable;
if (e == null || !e.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))) return false;
foreach (object _ in e)
{
return true;
}
return false;
}
答案 4 :(得分:2)
为了完整起见,为最近的 c# 版本(来自 c#8.0)添加最先进的答案:
static bool IsNotNullOrEmpty<T>(object obj) => obj is IEnumerable<T> seq && seq.Any();
static bool IsNullOrEmpty<T>(object obj) => !(obj is IEnumerable<T> seq && seq.Any());
这假设您会在调用站点中知道 T
。
如果您不知道 T
,则不应使用 IEnumerable<object>
,而应使用 IEnumerable
。像这样:
static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();
答案 5 :(得分:1)
您可以先检查对象是否像列表和数组一样实现ICollection
,因为检查其中一个的大小更便宜,因为它们具有Count
属性。如果不是,您可以检查它是否实现IEnumerable
,如果是,请创建一个枚举器,看看是否可以移动到第一个项目:
public static bool IsNullOrEmpty(object obj) {
ICollection c = obj as ICollection;
if (c != null) {
return c.Count == 0;
}
IEnumerable e = obj as IEnumerable;
if (e != null) {
return !e.GetEnumerator().MoveNext();
}
return false;
}
答案 6 :(得分:-1)
这适用于在某种意义上可枚举的所有类型:
public bool IsNullOrEmpty(object obj)
{
if (obj != null) // we can't be null
{
var method = obj.GetType().GetMethod("GetEnumerator");
if (method != null) // we have an enumerator method
{
var enumerator = method.Invoke(obj, null);
if (enumerator != null) // we got the enumerator
{
var moveMethod = enumerator.GetType().GetMethod("MoveNext")
if (moveMethod != null) // we have a movenext method, lets try it
return !(bool)moveMethod.Invoke(enumerator, null); // ie true = empty, false = has elements
}
}
}
return true; // it's empty, lets return true
}
这基本上与foreach
- 循环相同,即检查可枚举性,并没有真正对所涉及的类型进行说明。