Search code examples
c#arrayslistextension-methods

c# extension methods for generic array


I'm trying to make extension methods for generic array, so I could takeout random set of elements.
I made following extension methods for List<T> type and they work great, but I can't work out how to do exactly the same for generic array:

public static T Random<T>(this List<T> list)
{
    return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}

public static IEquatable Random<IEquatable>(this List<IEquatable> list, List<IEquatable> hits)
{
    int rand = GameManager.instance.functions.RandomInt(list.Count - 1);
    while (hits.Exists(h => h.Equals(list[rand])))
        rand = GameManager.instance.functions.RandomInt(list.Count - 1);
    return list[rand];
}

public static List<T> Random<T>(this List<T> list, int count)
{
    List<T> result = new List<T>();
    for (int i = 0; i < count; i++)
    {
        result.Add(list.Random());
    }
    return result;
}

public static List<IEquatable> RandomUnique<IEquatable>(this List<IEquatable> list, int count)
{
    List<IEquatable> result = new List<IEquatable>();
    for (int i = 0; i < count; i++)
    {
        result.Add(list.Random(result));
    }
    return result;
}

I tried to rework the first method like this:

public static IEnumerable Random<IEnumerable>(this IEnumerable list)

but it doesn't recognize list as an array so I can't get to it's length value.
I see a workaround, to do a List from Array, then get my random values and make array again, but it's seems like too much action for just taking eg. 2 random from 4 elements array.
Please advise

EDIT:
Thanks to Mathew in comments, I managed to construct the extension method for generic array correctly:

public static T Random<T>(this T[] list)
{
    return list[GameManager.instance.functions.RandomInt(list.Length - 1)];
}

But ultimately I'll play around with the Dmitry's answer and try to make these for IEnumerable. Thank you very much!

EDIT2:
Thanks to Zastai, I changed all methods so they work for both List and generic array:

public static T Random<T>(this IReadOnlyList<T> list)
{
    return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}

public static IEquatable Random<IEquatable>(this IReadOnlyList<IEquatable> list, List<IEquatable> hits)
{
    int rand = GameManager.instance.functions.RandomInt(list.Count - 1);
    while (hits.Exists(h => h.Equals(list[rand])))
        rand = GameManager.instance.functions.RandomInt(list.Count - 1);
    return list[rand];
}

public static List<T> Random<T>(this IReadOnlyList<T> list, int count)
{
    List<T> result = new();
    for (int i = 0; i < count; i++)
    {
        result.Add(list.Random());
    }
    return result;
}

public static List<IEquatable> RandomUnique<IEquatable>(this IReadOnlyList<IEquatable> list, int count)
{
    List<IEquatable> result = new();
    for (int i = 0; i < count; i++)
    {
        result.Add(list.Random(result));
    }
    return result;
}

Doesn't work for strings (as in "abcdefg".Random()), but for my needs it's not neccessary.


Solution

  • IEnumerable is specifically just a sequence of values, and has no length.

    IReadOnlyList on the other hand, is a list of values (so does have a length) and does not allow adding/removing values.

    A .NET array implements both.

    So if you change your extension methods to take IReadOnlyList<xxx> instead of List<xxx> they should automatically work on arrays too.