It's not guaranteed to continue working as well as it does.
It's highly unlikely that it would ever stop working as well as it does, because that change would require a change to the pre-computation of key values, which would be very expensive in many cases. As such, a change that broke it is unlikely to ever happen. It would also be an observable change in other ways, in terms of how often the selector is called. That sort of observable change isn't ruled out (I've had PRs to .NET Core that reduced how often selectors are called in other linq methods accepted) but the change would have to have a very strong benefit, especially since it would increase, rather than decrease, the number of calls.
It doesn't currently work very well, because Enumerable.OrderBy
is (unlike other OrderBy
methods in linq, such as that on Queryable
or in PLinq) guaranteed to give a stable ordering (equal values always in their original order) which gives you a bias.
It is useful for quick-to-write random orderings where the quality of the shuffle need only be pretty good. For a more robust shuffle use something like:
public static class Shuffler
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, int seed)
{
return new ShuffledEnumerable<T>(source, seed);
}
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
return new ShuffledEnumerable<T>(source);
}
private class ShuffledEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _source;
private int? _seed;
public ShuffledEnumerable(IEnumerable<T> source)
{
_source = source;
}
public ShuffledEnumerable(IEnumerable<T> source, int seed)
: this(source)
{
_seed = seed;
}
public IEnumerator<T> GetEnumerator()
{
Random rnd = _seed.HasValue ? new Random(_seed.GetValueOrDefault()) : new Random();
T[] array = _source.ToArray();
int count = array.Length;
for (int i = array.Length - 1; i > 0; --i)
{
int j = rnd.Next(0, i + 1);
if (i != j)
{
T swapped = array[i];
array[i] = array[j];
array[j] = swapped;
}
}
return ((IEnumerable<T>)array).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
You can improve it further again if you need cryptographic quality of randomness by replacing the use of Random
to a suitable cryptographic PRNG.