432 Stimmen

Behandlungswarnung bei möglicher Mehrfachaufzählung von IEnumerable

In meinem Code benötige ich eine IEnumerable<> mehrmals, was zu der ReSharper-Fehlermeldung "Mögliche mehrfache Aufzählung von IEnumerable ".

Beispiel-Code:

public List<object> Foo(IEnumerable<object> objects)
{
    if (objects == null || !objects.Any())
        throw new ArgumentException();

    var firstObject = objects.First();
    var list = DoSomeThing(firstObject);        
    var secondList = DoSomeThingElse(objects);
    list.AddRange(secondList);

    return list;
}
  • Ich kann die objects Parameter zu sein List und dann die mögliche Mehrfachaufzählung vermeiden, aber dann bekomme ich nicht das höchste Objekt, das ich bearbeiten kann.
  • Eine weitere Möglichkeit ist die Konvertierung der IEnumerable まで List zu Beginn der Methode:

 public List<object> Foo(IEnumerable<object> objects)
 {
    var objectList = objects.ToList();
    // ...
 }

Aber das ist nur peinlich .

Was würden Sie in diesem Szenario tun?

2voto

Mauro Sampietro Punkte 2580

Normalerweise überlade ich meine Methode mit IEnumerable und IList in dieser Situation.

public static IEnumerable<T> Method<T>( this IList<T> source ){... }

public static IEnumerable<T> Method<T>( this IEnumerable<T> source )
{
    /*input checks on source parameter here*/
    return Method( source.ToList() );
}

In den zusammenfassenden Kommentaren zu den Methoden wird darauf hingewiesen, dass der Aufruf von IEnumerable ein .ToList() ausführt.

Der Programmierer kann wählen, ob er .ToList() auf einer höheren Ebene einsetzt, wenn mehrere Operationen verkettet werden, und dann die IList-Überladung aufruft oder ob er dies meiner IEnumerable-Überladung überlässt.

1voto

Madruel Punkte 61

Wenn Sie nur das erste Element überprüfen müssen, können Sie es angucken, ohne die gesamte Sammlung zu durchlaufen:

public List<object> Foo(IEnumerable<object> objects)
{
    object firstObject;
    if (objects == null || !TryPeek(ref objects, out firstObject))
        throw new ArgumentException();

    var list = DoSomeThing(firstObject);
    var secondList = DoSomeThingElse(objects);
    list.AddRange(secondList);

    return list;
}

public static bool TryPeek<T>(ref IEnumerable<T> source, out T first)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    IEnumerator<T> enumerator = source.GetEnumerator();
    if (!enumerator.MoveNext())
    {
        first = default(T);
        source = Enumerable.Empty<T>();
        return false;
    }

    first = enumerator.Current;
    T firstElement = first;
    source = Iterate();
    return true;

    IEnumerable<T> Iterate()
    {
        yield return firstElement;
        using (enumerator)
        {
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }
        }
    }
}

0voto

Frank Schwieterman Punkte 23718

Ich mag den Ansatz von Methoden, die eine IReadOnlyCollection nehmen, wenn sie das Ding mehrfach aufzählen müssen. Ich würde gerne eine Utility-Methode anbieten, die jederzeit aufgerufen werden kann, um eine IEnumerable in eine IReadOnlyCollection zu verwandeln. Im Gegensatz zu ToArray() oder ToList() kann es die Kosten für zusätzliche Zuweisungen vermeiden, die den Typ der zugrunde liegenden Sammlung ändern:

    public static class EnumerableUtils
    {
        /* Ensures an IEnumerable is only enumerated once  */
        public static IReadOnlyCollection<T> AsEnumerated<T>(this IEnumerable<T> original)
        {
            if (original is IReadOnlyCollection<T> originalCollection)
            {
                return originalCollection;
            }

            return ImmutableArray.CreateRange(original);
        }
    }

-3voto

vidstige Punkte 11788

Zunächst einmal bedeutet diese Warnung nicht immer so viel. Ich habe sie normalerweise deaktiviert, nachdem ich mich vergewissert habe, dass es sich nicht um einen Leistungsengpass handelt. Es bedeutet nur, dass die IEnumerable wird zweimal ausgewertet, was normalerweise kein Problem darstellt, es sei denn, die evaluation selbst nimmt viel Zeit in Anspruch. Selbst wenn es lange dauert, wird in diesem Fall nur ein Element beim ersten Mal verwendet.

In diesem Szenario könnten Sie auch die leistungsstarken Linq-Erweiterungsmethoden noch mehr ausnutzen.

var firstObject = objects.First();
return DoSomeThing(firstObject).Concat(DoSomeThingElse(objects).ToList();

Es ist möglich, nur die IEnumerable einmal in diesem Fall mit etwas Mühe, aber Profil zuerst und sehen, ob es wirklich ein Problem ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X