programing

IEnumberable의 다중 열거 가능성에 대한 경고 처리

goodsources 2023. 5. 11. 21:25
반응형

IEnumberable의 다중 열거 가능성에 대한 경고 처리

내 코드에서 나는 사용해야 합니다.IEnumerable<>하여 " of 여러 걸쳐번의여에러성의, "개열가능거다니발오류합"의했습니다.IEnumerable".

샘플 코드:

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;
}
  • 는 할수있다니습경변▁the를 바꿀 수 있습니다.objects be 될개 변수매List가능한 다중 열거를 피하지만 내가 처리할 수 있는 가장 높은 개체를 얻지 못합니다.
  • 내가 할 수 있는 또 다른 일은 변환하는 것입니다.IEnumerableList메서드의 시작 부분:

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

하지만 이건 그냥 어색해요.

이 시나리오에서는 어떻게 하시겠습니까?

를 .IEnumerable호출자에게 "이것을 열거하고 싶습니다"라고 말하는 것이 매개 변수입니다.당신이 몇 번을 열거하고 싶은지는 그들에게 말하지 않습니다.

objects 매개 변수를 List로 변경한 다음 가능한 다중 열거를 피할 수 있지만 처리할 수 있는 가장 높은 개체를 얻지 못합니다.

가장 높은 대상을 차지하려는 목표는 고귀하지만, 너무 많은 가정의 여지를 남깁니다.LINQ to SQL 쿼리를 이 메서드에 두 번만 열거할 수 있도록(매번 다른 결과를 얻을 수 있음) 누군가가 이 메서드에 전달하기를 원하십니까?

여기서 의미론적으로 누락된 것은 아마도 메서드의 세부사항을 읽는 데 시간을 들이지 않는 호출자가 한 번만 반복한다고 가정할 수 있다는 것입니다. 그래서 그들은 값비싼 물건을 의미합니다.메소드 서명은 어느 쪽으로도 표시되지 않습니다.

를 메다서서다명변로경니합음으을로 으로써.IList/ICollection당신은 적어도 당신의 기대가 무엇인지를 전화를 건 사람들에게 더 명확하게 할 것이고, 그들은 비용이 많이 드는 실수를 피할 수 있습니다.

그렇지 않으면 메소드를 보고 있는 대부분의 개발자가 사용자가 한 번만 반복한다고 가정할 수 있습니다.을 보는 IEnumerable그것은 매우 중요합니다, 당신은 그것을 하는 것을 고려해야 합니다..ToList()방법의 시작 부분에서.

아쉽습니다.NET에는 추가/제거 등의 메서드가 없는 IEnumberable + Count + Indexer 인터페이스가 없으므로 이 문제가 해결될 것으로 생각됩니다.

데이터가 항상 반복 가능하다면 걱정할 필요가 없습니다.그러나 롤을 해제할 수도 있습니다. 들어오는 데이터가 클 경우(예: 디스크/네트워크에서 읽기) 특히 유용합니다.

if(objects == null) throw new ArgumentException();
using(var iter = objects.GetEnumerator()) {
    if(!iter.MoveNext()) throw new ArgumentException();

    var firstObject = iter.Current;
    var list = DoSomeThing(firstObject);  

    while(iter.MoveNext()) {
        list.Add(DoSomeThingElse(iter.Current));
    }
    return list;
}

참고 저는 DoSomething Else의 의미를 조금 바꿨지만, 이것은 주로 롤업되지 않은 사용법을 보여주기 위한 것입니다.예를 들어, 반복기를 다시 포장할 수 있습니다.있고,도 있습니다. 반블록만수있다습니도가 . 좋을 수도 있습니다. 그러면 없습니다.list은 리고당신은그은신▁would.yield return반품할 목록에 추가하는 것이 아니라 받는 대로 항목을 변경할 수 있습니다.

용사를 합니다.IReadOnlyCollection<T>또는IReadOnlyList<T>에서 대신 대신에IEnumerable<T>에는 반복하기 전에 카운트를 확인해야 하거나 다른 이유로 여러 번 반복해야 할 수 있음을 명시할 수 있는 장점이 있습니다.

그러나 인터페이스를 사용하기 위해 코드를 재팩터링하여 테스트하기 쉽고 동적 프록시에 친숙하게 만드는 등의 경우 문제가 발생할 수 있는 큰 단점이 있습니다.중요한 점은 에서 상속되지 않는다는 것이며, 다른 컬렉션과 각 읽기 전용 인터페이스에 대해서도 마찬가지입니다.(간단히 말해서, 이는.NET 4.5는 이전 버전과 ABI 호환성을 유지하기를 원했습니다.하지만 그들은 그것을 바꿀 기회조차 잡지 않았습니다.NET 코어.)

이것은 만약 당신이 그것을 얻는다면IList<T>.IReadOnlyList<T>,할 수 없습니다!그러나 as를 통과할 수 있습니다.

마내침.IEnumerable<T>sl에서 입니다.모든 수집 인터페이스를 포함하는 NET 컬렉션입니다.일부 아키텍처 선택에서 벗어날 수 없다는 것을 알게 되면 다른 대안이 다시 떠오를 것입니다.따라서 함수 서명에 사용하여 읽기 전용 컬렉션을 원하는 것을 표현하는 것이 적합하다고 생각합니다.

(항상 다음을 작성할 수 있습니다.)IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)두 를 모두 하는 경우 때는 .IEnumerable<T>항상 호환됩니다.)

이것이 절대적인 것은 아니기 때문에 실수로 여러 개의 열거를 하는 것이 문제가 되는 데이터베이스를 많이 사용하는 코드를 작성하는 경우에는 다른 절충을 선호할 수 있습니다.

.NET 6/ C# 10 포함

Enumerable을 사용하여 강제로 열거하지 않고 시퀀스의 요소 수를 결정할 수 있습니다.GetNonEumeratedCount(IEnumberable, Int32) 메서드를 사용해 보십시오.

이 메서드는 다음을 반환합니다.true의 경우에는source하지 않고 할 수 . 않으면 열하지않수있결다습니정할고거▁can다있니습수,▁otherwise▁be않▁enum. 그렇지 않으면,false따라서 추가 구현이 필요한지 확인할 수 있습니다.

using System;
using System.Collections.Generic;
using System.Linq;
                    
public class Program
{
    public static void Main()
    {
        IEnumerable<int> arrayOne = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        var canGetCountDirectly = arrayOne.TryGetNonEnumeratedCount(out int theCount);

        Console.WriteLine($"Count can be returned directly = {canGetCountDirectly}");
        Console.WriteLine($"Count = {theCount}");
    }
}

로 마크 를 유지하면서 ▁the▁remove▁if▁▁simple▁could▁you마,taining▁the▁the있ations▁than▁same▁semantics다니▁enumer습수▁but▁by▁multiple크▁main▁the▁answer▁to▁marc할▁the제▁read▁grave▁is거▁aim간히▁is단▁prevent▁really항을목▁one▁toll라중복의동유Any그리고.First통화 및 연결:

public List<object> Foo(IEnumerable<object> objects)
{
    if (objects == null)
        throw new ArgumentNullException("objects");

    var first = objects.FirstOrDefault();

    if (first == null)
        throw new ArgumentException(
            "Empty enumerable not supported.", 
            "objects");

    var list = DoSomeThing(first);  

    var secondList = DoSomeThingElse(objects);

    list.AddRange(secondList);

    return list;
}

은 당신이 "", ""라고 합니다.IEnumerable제네릭이 아니거나 적어도 참조 유형으로 제한됩니다.

저는 보통 IEnumberable로 메서드를 오버로드하고 이 상황에서 나열합니다.

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() );
}

IEnumberable을 호출하면 수행할 수 있는 방법에 대한 요약 주석을 사용하여 설명합니다.목록으로().

프로그래머는 을 선택할 수 있습니다.여러 작업이 연결되어 있는 경우 상위 수준의 ToList()를 선택한 다음 IList 오버로드를 호출하거나 내 IEnumberable 오버로드가 처리하도록 합니다.

첫 번째 요소만 확인하면 전체 컬렉션을 반복하지 않고 볼 수 있습니다.

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;
            }
        }
    }
}

IReadOnlyCollection을 사용하는 메소드는 여러 번 열거해야 하는 경우에 좋습니다.IE numberable을 IReadOnlyCollection으로 전환하기 위해 언제든지 호출할 수 있는 유틸리티 메소드를 제공하고자 합니다.ToArray()나 ToList()와는 달리 기본 컬렉션의 유형을 변경하는 추가 할당 비용을 피할 수 있습니다.

    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);
        }
    }

먼저, 그 경고가 항상 그렇게 큰 의미는 아닙니다.저는 보통 성능의 보틀 넥이 아닌 것을 확인한 후 비활성화했습니다.그것은 단지 의미합니다.IEnumerable두 번 평가되며, 이는 보통 다음이 아닌 한 문제가 되지 않습니다.evaluation시간이 오래 걸립니다.시간이 오래 걸리더라도 이 경우에는 처음에 하나의 요소만 사용합니다.

이 시나리오에서는 강력한 linq 확장 방법을 더욱 활용할 수 있습니다.

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

▁it▁only▁to▁▁is만 평가할 수 있습니다.IEnumerable이 경우에 한 번 번거롭지만, 먼저 프로파일링하고 정말 문제가 있는지 확인하십시오.

언급URL : https://stackoverflow.com/questions/8240844/handling-warning-for-possible-multiple-enumeration-of-ienumerable

반응형