我无法做我想做的事。我只想要非国际代表的账户。但是当我调用ActiveAccounts()时,我没有得到null,我得到一个可枚举的,然后包含null。我在这做错了什么?请帮助。
public static class AccountExt
{
public static IEnumerable<Account> ActiveAccounts( this AccountRep rep )
{
if( rep == null )
throw new ArgumentNullException();
if( rep.IsInternational )
yield return null;
foreach( var acc in rep.FetchAccounts() )
{
if( acc.IsActive )
yield return acc;
}
}
}
答案 0 :(得分:8)
嗯,这里有几件事情。
首先,您不只是有一个扩展方法,您有一个扩展方法迭代器块 - 这是您使用yield return
时得到的内容自动实施IEnumerable<>
合同。
听起来你想要发生的是ActiveAccounts()
返回null IEnumerable<Account>
。实际发生的是,对于国际代表,您将返回null 作为IEnumerable 的第一个元素。我怀疑你可能
我曾尝试在那里使用return null
但是遇到了类似的编译器错误:
错误:无法从迭代器返回值。使用yield return语句返回一个值,或者使用yield break来结束迭代。
如果你想要的是可枚举的空白,你想要的是yield break
而不是yield return null
。通常,返回空序列实际上是一个更好的主意,因为它允许调用者避免检查返回值。它还使用LINQ等技术更好地运行,LINQ使用组合来组合复杂的查询。
第二个问题是,当您调用if( rep == null )
时,不会评估ActiveAccounts()
前置条件,而是在您开始枚举该调用的结果时。这可能不是你想要的 - 我想你想要立即评估前提条件。
解决这两个问题的方法是使用两阶段实现:
public static class AccountExt
{
// apply preconditions, return null for international reps
public static IEnumerable<Account> ActiveAccounts( this AccountRep rep )
{
if( rep == null )
throw new ArgumentNullException( "rep" );
if( rep.IsInternational )
return null;
// otherwise...
return ActiveAccountsImpl( rep );
}
// private implementation handles returning active accounts
private static IEnumerable<Account> ActiveAccountsImpl( AccountRep rep )
{
foreach( acc in rep.FetchAccounts() )
{
if( acc.IsActive )
yield return acc;
}
}
}
如果您愿意使用LINQ,可以避免使用Impl
版本的函数:
public static IEnumerable<Account> ActiveAccounts( this AccountRep rep )
{
if( rep == null )
throw new ArgumentNullException( "rep" );
if( rep.IsInternational )
return null;
// otherwise, using LINQ to filter the accounts...
return rep.FetchAccounts().Where( acc => acc.IsActive );
}
您可以详细了解迭代器如何阻止here。
答案 1 :(得分:5)
您应该将yield return null
替换为yield break
。这将返回一个空序列。
如果你真的想要返回null
而不是IEnumerable
,那么LBushkin的答案是你想要的答案;但是,更常见的做法是返回一个空序列,因为它不需要消费者检查返回值。