In the following code, if you uncomment the "using static" line, the query will not run in parallel. Why?
(Visual Studio Community 2019, .Net Core 3.1 / .Net 4.8)
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace UsingStatic_Mystery
{
//using static Enumerable;
class Program
{
static void Main(string[] args)
{
var w = new Stopwatch();
iter:
w.Start();
var xx = Enumerable.Range(0, 10)
.AsParallel()
.OrderByDescending(x => {
Thread.Sleep(new Random().Next(100));
Console.WriteLine(x);
return x;
}).ToArray();
w.Stop();
Console.WriteLine();
foreach (var x in xx) Console.WriteLine(x);
Console.WriteLine(w.ElapsedMilliseconds);
Console.ReadLine();
w.Reset();
goto iter;
}
}
}
output, uncommented/commented:


Found:
This is the IL code generated with the
using staticcommented (so nousing static):and this is the IL code generated with the
using staticuncommented (so withusing static):The "correct" side is using
Parallel.OrderBy, the "wrong" side is usingEnumerable.OrderBy. The result you see is quite clearly for this reason. And the reason for why one or the otherOrderByis selected is because with theusing static Enumerableyou declare that the C# should prefer methods in theEnumerableclass.More interestingly, had you written the using block like this:
so outside the namespace, everything would have worked "correctly" (IL code generated).
I'll say that namespace resolution works by level... First C# tries all the
usingdefined in the innermost level ofnamespace, if there is no one that is good enough then it goes up a level ofnamespace. If there are multiple candidates in the same level it takes the best match. In the example without theusing staticand the example I gave where theusing+ theusing staticare all top-level, there is a single level, so the C# takes the best candidate. In the two-levelsusingthe innermost one is checked, and theusing static Enumerableis good enough to resolve theOrderBymethod, so no extra checking is done.I'll say that this time again, SharpLab was the MVP of this response. If you have a question about what the C# compiler does under the hood, SharpLab can give you the response (technically you could use ildasm.exe or ILSpy, but SharpLab is very immediate because it is a web site, and you can interactively change the source code). The SVP (second valuable player) (for me) was WinMerge, that I used to compare the IL assemblies
Answer to the comment
The C# 6.0 draft reference page says
and then
so the first rule is applied even to
using static. This explains why the third example (mine) is equivalent to the no-using static.About why
ParallelEnumerable.OrderedBy()is better thanEnumerable.OrderBy()when both of them are checked by the C# compiler, it is simple:The
AsParallel()returns aParallelQuery<TSource>(that implementsIEnumerable<TSource>)The
ParallelEnumerable.OrderedBy()signature:The
Enumerable.OrderedBy()signature:The first one accepts a
ParallelQuery<TSource>, that is the exact same type returned byAsParallel(), no "downcast" necessary.