using System.Collections.Generic;
public static class TypeExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
public static void Main()
TestWithToListAndWithoutExtraAsParallel();
TestWithToListAndWithAsEnumerableBeforeToList();
TestWithToListReversed();
PrintDebugInfo(ParallelEnumerable.Range(0, 50), "ParallelEnumerable.Range()");
PrintDebugInfo(ParallelEnumerable.Range(0, 50).AsParallel(), "ParallelEnumerable.Range().AsParallel()");
PrintDebugInfo(ParallelEnumerable.Range(0, 50).AsParallel().WithExecutionMode(ParallelExecutionMode.ForceParallelism), "ParallelEnumerable.Range().AsParallel().WithExecutionMode()");
PrintDebugInfo(ParallelEnumerable.Range(0, 50).WithExecutionMode(ParallelExecutionMode.ForceParallelism), "ParallelEnumerable.Range().WithExecutionMode()");
TestWithToList(1222, false);
static void TestWithToList(int count = 50, bool printEachNumber = true)
var numbers = ParallelEnumerable.Range(0, count);
var divisibleBy5Query = numbers
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
var divisibleBy5 = divisibleBy5Query
Console.WriteLine("\nNumbers divisible by 5 with ToList() ({0}.OrdinalIndexState = {1}):", divisibleBy5Query, GetOrdinalIndexState(divisibleBy5Query));
Console.WriteLine("\nIteration of {0} Numbers divisible by 5 with ToList() ({1}.OrdinalIndexState = {2}):", count, divisibleBy5Query, GetOrdinalIndexState(divisibleBy5Query));
PrintNumbers(divisibleBy5, printEachNumber);
static void TestWithoutToList()
var numbers = ParallelEnumerable.Range(0, 50);
var divisibleBy5Query = numbers
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
var divisibleBy5 = divisibleBy5Query
Console.WriteLine("\nNumbers divisible by 5 without ToList() ({0}.OrdinalIndexState = {1}):", divisibleBy5Query, GetOrdinalIndexState(divisibleBy5Query));
PrintNumbers(divisibleBy5);
static void TestWithToListAndWithoutExtraAsParallel()
var numbers = ParallelEnumerable.Range(0, 50);
var divisibleBy5Query = numbers
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
var divisibleBy5 = divisibleBy5Query
Console.WriteLine("\nNumbers divisible by 5 with ToList() but without the extra AsParallel() ({0}.OrdinalIndexState = {1}):", divisibleBy5Query, GetOrdinalIndexState(divisibleBy5Query));
PrintNumbers(divisibleBy5);
static void TestWithToListAndWithAsEnumerableBeforeToList()
var numbers = ParallelEnumerable.Range(0, 50);
Console.WriteLine("\nNumbers divisible by 5 with cast to IEnumerable<int> before the ToList():");
IEnumerable<int> divisibleBy5Query = numbers
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
var divisibleBy5 = divisibleBy5Query
PrintNumbers(divisibleBy5);
static void TestWithToListReversed()
var numbers = ParallelEnumerable.Range(0, 50);
var divisibleBy5Query = numbers
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
var divisibleBy5 = divisibleBy5Query
Console.WriteLine("\nNumbers divisible by 5 with Reverse() and then ToList() ({0}.OrdinalIndexState = {1}):", divisibleBy5Query, GetOrdinalIndexState(divisibleBy5Query));
PrintNumbers(divisibleBy5);
static void PrintDebugInfo()
var numbers = ParallelEnumerable.Range(0, 50);
var divisibleBy5Query = numbers
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
PrintDebugInfo(divisibleBy5Query, nameof(divisibleBy5Query), true);
static void PrintNumbers(IEnumerable<int> query, bool printEachNumber = true)
int previous = int.MinValue;
foreach (var number in query)
Console.Write(number + "\t");
if (previous > int.MinValue)
Console.Write(ordered ? "ordered" : "not ordered");
static void PrintDebugInfo(object divisibleBy5, string divisibleBy5Name, bool includeInterfaces = false)
Console.WriteLine("\n\nType hierarchy of {0}:", divisibleBy5Name);
foreach (var t in divisibleBy5.GetType().BaseTypesAndSelf())
Console.Write(" {0}", name);
if (name.StartsWith("System.Linq.Parallel.QueryOperator`1"))
Console.Write(", OrdinalIndexState = {0}", GetOrdinalIndexState(divisibleBy5));
var enumeratorMethod = divisibleBy5.GetType().GetMethod("GetEnumerator", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy, new [] { typeof(ParallelMergeOptions?) });
Console.Write(", Enumerator type: {0}", enumeratorMethod?.Invoke(divisibleBy5, new object [] { ParallelMergeOptions.FullyBuffered }));
Console.WriteLine("Interfaces implemented by {0}:", divisibleBy5Name);
foreach (var iType in divisibleBy5.GetType().GetInterfaces())
Console.WriteLine(" " + iType.ToString());
Console.WriteLine("GetEnumerator methods of {0}:", divisibleBy5Name);
foreach (var method in divisibleBy5.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.Name.Contains("GetEnumerator")))
Console.WriteLine(" {0}: declared by {1}", method, method.DeclaringType);
static string GetOrdinalIndexState(object query)
var property = query.GetType().GetProperty("OrdinalIndexState", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance| BindingFlags.FlattenHierarchy);
var ordinalIndexState = property?.GetValue(query);
return ordinalIndexState?.ToString();