例如,如果我有这种方法:
IEnumerable<int> GetRandomNumbers()
{
// {Codes that generate numbers as List<int>}
if(generationFails == true)
{
return Enumberable.Empty<int>(); // I do this to signal that we have an error
}
return numbers;
}
在调用方法中我做:
IEnumerable<int> AddNumber(int number)
{
var random = GetRandomNumbers();
var randomList = random as IList<int> ?? random.ToList(); // Run ToList only if needed
randomList.Add(number);
return randomList;
}
当生成失败时,我得到一个异常“[NotSupportedException:Collection是固定大小的。]”。
这是因为Enumerable empty是一个IList,因此.ToList()没有运行,然后我尝试添加到一个固定的Enumberable.Empty。我错误地认为这是一个糟糕的设计,一个继承IList的对象(其中定义了Add)应该支持Add?
我被迫var randomList = random.ToList()
或停止使用Enumberable.Empty
吗?还有更好的方法吗?
更新 我想在我的例子中我不清楚。我希望吞下(或记录)错误,但允许操作继续而不会崩溃。我的评论“我这样做是为了表明我们有错误”是为了告诉其他开发人员阅读代码,这是一种异常行为。
蒂姆所关联的问题的answer就是我所得到的。似乎我们没有用于常量集合的接口,因此使用了IList
。
答案 0 :(得分:5)
更好的方法是返回null
或抛出异常。返回空列表可以被认为是有效的替代,但不是在您的方法的上下文中(例如,过滤另一个没有有效项目的列表)。
失败的随机数生成似乎表明生成算法存在问题,应该抛出异常,而不是空列表。
如果您总是希望找到一个值,那么抛出异常(如果缺少)。例外意味着存在问题。
如果值可能丢失或存在且两者都对应用程序逻辑有效,则返回null。
答案 1 :(得分:2)
实际上Enumerable.Empty
returns an empty array,这就是Array.IList.Add
中获得NotSupportedException
的原因。数组具有固定的大小。
如果您想要发出错误信息,我会返回null
而不是空序列。在可读性方面,对业务逻辑的类型检查并不好。
if(generationFails == true)
{
return null; // I do this to signal that we have an error
}
然后很容易:
IEnumerable<int> random = GetRandomNumbers();
IList<int> randomList = random == null ? new List<int>() : random.ToList();
空序列表明一切都很好。考虑您将来更改方法以获取整数size
。现在有人提供0
作为大小,它也返回一个空序列。您不能再区分错误和空序列。
当然,您也可以返回new List<int>
而不是Enumerable.Empty<int>
。
答案 2 :(得分:2)
解决此问题的另一种方法是使用yield break
返回空的枚举器:
IEnumerable<int> GetRandomNumbers()
{
if (generationFails)
yield break;
foreach (var element in numbers)
{
yield return element;
}
}
这将使您的IEnumerable<int>
懒惰地返回每个随机数。
请注意,这不会将错误单独添加到调用代码中。如果generationFails
应该在代码执行中单个错误,那么你应该抛出异常,正如其他人所说的那样。
答案 3 :(得分:0)
使用Enumerable.Empty()方法,您可以使用缓存数组而不是创建一个新数组。这可能会对性能产生积极影响,因为您的代码将很少打扰垃圾收集器。
看看Enumerable.Empty() vs new ‘IEnumerable’() – what’s better?