为什么这种扩展方法不起作用?

mso*_*hie 3 .net c# extension-methods .net-3.5

我不能做我想做的事.我只想要非国际代表的账户.但是当我调用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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

LBu*_*kin 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;
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

如果您愿意使用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 );
   }
Run Code Online (Sandbox Code Playgroud)

您可以在此处了解有关迭代器如何阻止的更多信息.


Aar*_*ght 5

此时应更换yield return null使用yield break.这将返回一个空序列.

如果你真的想要返回null而不是IEnumerable当时LBushkin的答案是你想要的那个; 但是,更常见的做法是返回一个空序列,因为它不需要消费者必须检查返回值.