Could blocking an enum potentially cause a multiple enumeration?

I think ReSharper is lying to me.

I have this extension method which (hopefully) returns xor of two enums:

public static IEnumerable<T> Xor<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
    lock (first)
    {
        lock (second)
        {
            var firstAsList = first.ToList();
            var secondAsList = second.ToList();

            return firstAsList.Except(secondAsList).Union(secondAsList.Except(firstAsList));
        }
    }
}

      

ReSharper thinks I am doing multiple IEnumerable enumeration, as you can see, on both arguments. If I remove the locks, then he is satisfied that I am not.

Multiple enumeration

Is ReSharper right or wrong? I believe this is wrong.


edit: I realize I am listing the lists multiple times, but ReSharper says that I am listing the original arguments multiple times, which I don't think is true. I enumerate both arguments once into a list so that I can then do the actual manipulation of the set, but as I can see I am not actually iterating over the arguments passed multiple times.

For example, if the arguments passed are actually the results of a request, I believe this method will not cause a storm of requests to be executed by the given manipulation. I understand what ReSharper means by warning about multiple enumerations: if passed enumerators are heavy to generate, then if they are enumerated multiple times, then executing multiple enumerations on them will be much slower.

Plus, removing locks makes ReSharper happier:

No multiple enumeration

+3


source to share


2 answers


You are indeed listing both lists multiple times. You are not listing enumerations passed as parameters multiple times. Both lists are listed once for each call Except

. The call Union

does not list another sequence of extra time, but lists the results of the two calls Except

.

Of course, iterating List

multiple times in a context like this isn't really a problem; there is no downside to repeating an immutable list multiple times.



Operators lock

have nothing to do with sequence enumeration. Locking on IEnumerable

does not iterate. Of course, locking on two objects like this one, specifically two objects that are not limited in the scope of this section of code, are very dangerous. It is entirely possible to end the dead end of the program with locks used in this manor if code elsewhere (like another call to this method) ends up with locks on the same objects in reverse order).

+2


source


It's a little funny.

First of all: as you've correctly identified, R # raises this check not against multiple orders of List

s - of course there is nothing to worry about multiplying with multiplying a List

- but against (which R 'considers multiple uses) arguments IEnumerable

. I assume that you already know why this would be nice, so I'll skip it.


Now the question is whether R # is right to complain here. To quote the C # spec,

Form locking operator

lock (x) ...

      

where x

is a reference type expression, strictly equivalent to

System.Threading.Monitor.Enter(x);
try {
  ...
}
finally {
  System.Threading.Monitor.Exit(x);
}

      

except that it x

is evaluated only once.

(I made this emphasis because I like this formulation, it avoids discussions (which I am definitely not qualified to type) about whether this is "syntactic sugar" or not.)

Taking a minimal example that does this R # check:

private static void Method(IEnumerable<int> enumerable)
{
    lock (enumerable)
    {
        var list = enumerable.ToList();
    }
}

      

and replacing it with what I think is exactly the equivalent version as stated by the spec:



private static void Method(IEnumerable<int> enumerable)
{
    var x = enumerable;
    System.Threading.Monitor.Enter(x);
    try
    {
        var list = enumerable.ToList();
    }
    finally
    {
        System.Threading.Monitor.Exit(x);
    }
}

      

also checks.

The question is, is there R # right to do this check? And here I think that we find ourselves in a gray space. When I pass the following enum for any of these methods:

static IEnumerable<int> MyEnumerable()
{
    Console.WriteLine("Enumerable enumerated");
    yield return 1;
    yield return 2;
}

      

multiplied by not , which suggests that R # is wrong to warn here; however, I cannot find anything in the documentation that guarantees this behavior lock

, either Monitor.Enter

. So it's not quite as clear to me as this R # bug I reported where the usage is GetType

flagged by this check
; but nevertheless, I think you are safe.

If you bring this up in the R # bug tracker , you can get a better view of JetBrains: a) is this behavior really guaranteed. and b) whether the R # can be adjusted to either not warn or justify the warning.


This of course, the use of blocking here probably doesn't actually achieve what you want to achieve as stated in other answers and comments ...

+1


source







All Articles