C# 检查泛型IEnumerable是否为空
假设我有一个对象,它的类型可能是C# 检查泛型IEnumerable是否为空,c#,linq,generics,C#,Linq,Generics,假设我有一个对象,它的类型可能是IEnumerable。我想编写一个方法,如果对象的类型为IEnumerable、不为null且不为空,则返回true 以下是到目前为止我得到的信息: public bool IsNullOrEmpty(object obj) { if (obj != null) { if (obj is IEnumerable<object>) { return (obj as IEnumera
IEnumerable
。我想编写一个方法,如果对象的类型为IEnumerable
、不为null且不为空,则返回true
以下是到目前为止我得到的信息:
public bool IsNullOrEmpty(object obj)
{
if (obj != null)
{
if (obj is IEnumerable<object>)
{
return (obj as IEnumerable<object>).Any();
}
}
return false;
}
public bool为空或空(object obj)
{
如果(obj!=null)
{
if(对象是IEnumerable)
{
return(对象为IEnumerable).Any();
}
}
返回false;
}
如果传入类型为List
的对象,则此操作有效,但如果传入类型为List
的对象,则此操作无效。它失败是因为obj是IEnumerable
返回false
你知道我如何使所有的泛型IEnumerable
s都可以这样做吗?继承自-因此,如果你可以检查非泛型IEnumerable
,而不是泛型IEnumerable
,你可以直接转换到IEnumerable
关于代码的几点注意事项:首先用检查is
,然后用转换为
。这通常是不必要的as
已经检查,如果强制转换失败,则返回null
。因此,较短的方法是:
var enumerable = obj as IEnumerable;
if (enumerable != null) {
return !enumerable.Cast<object>().Any();
}
var enumerable=obj作为IEnumerable;
if(可枚举!=null){
return!enumerable.Cast().Any();
}
请注意,您需要对那里进行额外的调用,因为需要一个通用的
IEnumerable
由于类型可能未知,您可以尝试检查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;
}
您可以尝试将其强制转换为
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;
}
您可以检查非泛型
IEnumerable
并检查其是否为空。您可以添加检查以确保对象使用反射实现IEnumerable
:
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;
}
publicstaticbool为空或空(objectobj)
{
var e=对象作为System.Collections.IEnumerable;
如果(e==null | |!e.GetType().GetInterfaces().Any(i=>i.IsGenericType&&i.GetGenericTypeDefinition()==typeof(IEnumerable))返回false;
foreach(对象在e中)
{
返回true;
}
返回false;
}
您可以首先检查对象是否实现了与列表和数组类似的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;
}
这回答了将问题扩展到
IEnumerable
范围之外的问题,即“我能在foreach
-循环中使用这个东西吗?”
您可能知道,foreach
主要不检查IEnumerable
,而是尝试枚举给定的内容
这将适用于在某种意义上可枚举的所有类型,foreach
认为:
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-循环相同,即检查可枚举性,并不真正说明所涉及的类型
因此,确实应该避免这种情况,但如果您必须检查是否可以将键入的内容放入
foreach
-循环,则应该可以做到这一点。为完整起见,请为最新的c版本(来自c 8.0)添加最先进的答案:
坦率地说,“修复”这个问题是个坏主意;在一般情况下,您不应假设
IEnumerable[]
是可重复的,在这种情况下:询问。Any()
可能意味着您永远无法询问实际数据如果它是包含一个元素的列表,false
这不会破坏函数吗?如果你询问枚举是否为空,你不应该返回Any()
的否定结果吗?@DakotahHicock No-Any
没有lambda参数,它不会尝试计算内容-它只会检查是否存在任何成员。@marcgravel:,对any()
的任何调用都是如此,不管是在这里显示的方法之内还是之外。你不能在对象上调用它。
@RobertHarvey true,我没有意识到类型事先不知道。我将尝试并更新。您的方法不会尝试处理使用GetEnumerator
创建的IEnumerator
enumerable是IEnumerable
实际上已经执行了空检查,因此如果(enumerable!=null),您可以删除该行(:如果提供的表达式为非null,则is表达式的计算结果为true,并且可以将提供的对象强制转换为提供的类型,而不会引发异常。)我不知道您可以将扩展方法附加到任意的t
类型。该扩展方法会出现在任何类中吗?为什么这应该是泛型方法?您没有在方法中使用typet
,并且由于您没有尝试将null
分配给t
类型的任何标识符,因此您没有使用任何@O.R.Mapper:为什么允许它在int
这样的值类型上使用它?使用类约束需要是泛型的。@RobertHarvey:仅用于引用类型。因此,您可以“”.IsNullOrEmpty()
但不能1.IsNullOrEmpty()
@TimSchmelter:为什么不允许它在int
这样的值类型上使用它呢?你不想在MoveNext
调用后处理IEnumerator
,如果它是IDisposable
,foreach
总是这样做的。@NathanA:s/IEnumerable/IEnumerator//code>?@NathanA-I也可以,但非泛型的IEnumerator
n
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;
}
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
}
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());
static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();