I assume that you're talking about LINQ-to-Objects, in which case the key used for comparison is only generated once per element. (Note that this is just a detail of the current implementation, and could change, although it's very unlikely to because such a change would introduce the bugs that you mention.)
To answer your more general question: your approach should work, but there are better ways to do it. Using OrderBy
will typically be O(n log n) performance, whereas a Fisher-Yates-Durstenfeld shuffle will be O(n):
var shuffledArray = myArray.Shuffle().ToArray();
// ...
public static class EnumerableExtensions
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
return source.Shuffle(new Random());
}
public static IEnumerable<T> Shuffle<T>(
this IEnumerable<T> source, Random rng)
{
if (source == null) throw new ArgumentNullException("source");
if (rng == null) throw new ArgumentNullException("rng");
return source.ShuffleIterator(rng);
}
private static IEnumerable<T> ShuffleIterator<T>(
this IEnumerable<T> source, Random rng)
{
T[] buffer = source.ToArray();
for (int n = 0; n < buffer.Length; n++)
{
int k = rng.Next(n, buffer.Length);
yield return buffer[k];
buffer[k] = buffer[n];
}
}
}
(And it's easy enough, and slightly more efficient, to create equivalent methods to perform an in-place shuffle on IList<T>
, if you prefer.)