简体   繁体   中英

How to create IEnumerable<T> on which multiple enumerations are not possible?

When I enumerate over an IEnumerable twice Resharper complains about Possible multiple enumerations of IEnumerable . I know, in some case of DB-queries when you enumerate twice you get an exception.

I want to reproduce that behavior in tests. So, I basically want the following function to throw (because of multiple enumerations):

    private void MultipleEnumerations(IEnumerable<string> enumerable)
    {
        MessageBox.Show(enumerable.Count().ToString());
        MessageBox.Show(enumerable.Count().ToString());
    }

What should I pass to it? All the Lists, Collections etc. are ok with multiple enumerations. Even this kind of IEnumerable doesn't give an exception:

    private IEnumerable<string> GetIEnumerable()
    {
        yield return "a";
        yield return "b";
    }

Thanks.

You probably just want a custom class:

public class OneShotEnumerable<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> _source;
    private bool _shouldThrow = false;

    public OneShotEnumerable(IEnumerable<T> source)
    {
        this._source = source;
    }

    public IEnumerator<T> GetEnumerator()
    {
        if (_shouldThrow) throw new InvalidOperationException();
        _shouldThrow = true;

        return _source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Create your own class that implements IEnumerable<T> and throw an exception if GetEnumerator() is called twice (use a boolean instance field).

Alternatively, create an iterator that uses a flag field to ensure that it cannot be called twice (enumerating an iterator twice will execute the entire method twice).

The custom class, which I've copied from John Gietzen's answer (with a couple of corrections), could usefully be combined with an extension method to create a really simple way to do this.

public class OneShotEnumerable<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> source;
    private bool shouldThrow = false;

    public OneShotEnumerable(IEnumerable<T> source)
    {
        this.source = source;
    }

    public IEnumerator<T> GetEnumerator()
    {
        if (shouldThrow) 
            throw new InvalidOperationException("This enumerable has already been enumerated.");

        shouldThrow = true;

        return this.source.GetEnumerator();
    }
}

public static clas OneShotEnumerableExtension
{
    public static IEnumerable<T> SingleUse<T>(this IEnumerable<T> source)
    {
#if (DEBUG)
        return new OneShotEnumerableExtension(source);
#else
        return source;
#endif
    }
}

Then you can pass something to your previous method by simply doing

MultipleEnumerations(MyEnumerable.SingleUse());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM