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

时间:2010-02-12 00:21:28

标签: c# .net .net-3.5 extension-methods

我无法做我想做的事。我只想要非国际代表的账户。但是当我调用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;
        }
    }
}

2 个答案:

答案 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的答案是你想要的答案;但是,更常见的做法是返回一个空序列,因为它不需要消费者检查返回值。