Почему этот метод расширения не работает? - PullRequest
3 голосов
/ 12 февраля 2010

Я не могу сделать то, что хочу. Я хочу только учетные записи для не международных представителей. Но когда я вызываю ActiveAccounts (), я не получаю нулевое значение, я получаю перечислимое, которое затем включает нулевое значение. Что я здесь не так делаю? Помогите пожалуйста.

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 ]

8 голосов
/ 12 февраля 2010

Ну, здесь происходит пара вещей.

Во-первых, у вас есть не просто метод расширения , у вас есть метод расширения блок итератора - это то, что вы получаете, когда используете yield return для автоматической реализации IEnumerable<> контракт.

Звучит так, как будто вы хотите, чтобы ActiveAccounts() вернул ноль IEnumerable<Account>. На самом деле происходит то, что для международных представителей вы возвращаете нуль в качестве первого элемента IEnumerable . Я подозреваю, что вы можете попытался использовать return null там, но получил ошибку компилятора что-то вроде:

Ошибка: невозможно вернуть значение из итератора. Используйте оператор yield return для возврата значения или yield break для завершения итерации.

Если вы хотите, чтобы перечисляемое было пустым , то вам нужно yield break вместо yield return null. Обычно лучше возвращать пустую последовательность, поскольку это позволяет вызывающей стороне избегать проверки возвращаемого значения. Он также лучше подходит для таких технологий, как 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 );
   }

Подробнее о том, как блокировать итераторы , можно узнать здесь .

5 голосов
/ 12 февраля 2010

Вы должны заменить yield return null на yield break. Это вернет пустую последовательность.

Если вы действительно хотите вернуть null вместо IEnumerable, тогда ответом Л.Бушкина будет тот, который вам нужен; тем не менее, более распространенной практикой является возврат пустой последовательности, поскольку от потребителя не требуется проверять возвращаемое значение.

...