0

We work a lot of arrays so most of out LINQ queries end up in the suffix .ToArray(). I want to introduce a "shadow" library of LINQ methods producing arrays directly instead of the intermediate enumerables.

static class EnumerableExtensions
{
  public static R[] SelectToArray<S,R>(this S[] self, Func<S, R> func) 
    => self.Select(func).ToArray();
}

Then I realized that I don't really want to distinguish between Select() and SelectToArray(), so I changed the name accordingly. However, I'll end up in a recurrent call never going for the original version, as I've hid that with my extension method.

The best I could come up with is to trick C# to believe that I'm still dealing with IEnumerable<S> and not S[].

static class EnumerableExtensions
{
  public static R[] Select<S,R>(this S[] self, Func<S, R> func) 
    => self.AsEnumerable().Select(func).ToArray();
}

My bother with it is twofold. First of all, it seems fishy to convert like that taking an array, making it an enumerable and then back to array. It's code smell to me but I'm not able to determine that.

Secondly, supposing that the above is fine and may be used, how do I enable C# to understand which version I'm looking for in the following sample? (I'm a strong advocate of explicit types but knowing that it's opinionated, I'd like to be able to explain to someone not sharing my sentiment.)

IEnumerable<string> thisIsEnumerable = array.Select(a => a.Property);
string[] thisIsArray                 = array.Select(a => a.Property);
var whatIsThis                       = array.Select(a => a.Property);

I know what it will be but how do I make the var'ed thing to assume the original type (that would be in effect if we eliminate the extension method)?

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • 1
    Is all of this simply to avoid a `ToArray()` call? I would **highly** discourage this as you are trying to change the meaning of the very well-established LINQ functions which will only cause confusion for new as well as experienced C# developers working on your code base. Additionally, it forces an `IEnumerable` to be converted to an array whether you want it or not, which I can only imagine will raise problems down the road. – Xerillio Apr 09 '23 at 09:32
  • @Xerillio Thank you for the comment. Please note that the question isn't requesting opinionated suggestions. While I agree with you in a general sense, on occasion, it **does** make sense to make such an deviation. In our project (which you're not familiar with but I am), it is the case. As for what will confuse whom in the future, that's hard to tell, isn't it? All the seniors here agreed that it's appropriate in our case but I'll bring your point into consideration. Now, would you also have suggestion on the question of dealing with `var` alternative? – Konrad Viltersten Apr 09 '23 at 09:52
  • 1
    I'm not sure I understand that part of the question. The compiler always chooses [the most specific overload](https://stackoverflow.com/a/32892303/3034273), so the return type of all three examples is a `string[]` as that is what your overload (extension) is returning. You can't change your method signature to explicitly return an `IEnumerable` if you want `thisIsArray` to work as well. If this is your only question and you don't want our input about your reasoning for doing this, then the first half of the question is irrelevant. – Xerillio Apr 09 '23 at 10:22
  • @Xerillio I see what's confusing in my question. I explained it in text but forgot to correct the sample corresponding to that, which made the description ambiguous. The mistake is now corrected, please take a peek at the "new" version of the extension method. I want it to be **precisely** as the name of the commonly known `Select()` and not `SelectToArray()` (as discussed in the context of our specific project where it makes sense). – Konrad Viltersten Apr 09 '23 at 12:10

2 Answers2

2

For what it's worth, you could use Array.ConvertAll for your extension method, which is more efficient than using Select...ToArray because it knows the size of the result array:

public static class EnumerableExtensions
{
    public static TResult[] SelectToArray<TInput, TResult>(this TInput[] array, Func<TInput, TResult> converter)
    {
        return Array.ConvertAll<TInput, TResult>(array, new Converter<TInput, TResult>(converter));
    }
}

Sample:

int[] inputArray = {1,2,3};
string[] resultArray = inputArray.SelectToArray(i => i.ToString());

You could also provide one for lists:

public static List<TResult> SelectToList<TInput, TResult>(this List<TInput> list, Func<TInput, TResult> converter)
{
    return list.ConvertAll<TResult>(new Converter<TInput, TResult>(converter));
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
1

May I suggest an alternative, that shows clear intent (easy-to-guess return types) and doesn't confuse the well-known LINQ functions:

static class EnumerableExtensions
{
  public static R[] ToArray<S,R>(this IEnumerable<S> self, Func<S, R> selector) 
    => self.Select(selector).ToArray();
}

Usage:

var names = peopleCollection.ToArray(p => p.Name);

Unless your problem is deeper than currently described, this seems to solve the problem by removing the same amount (8) of characters (Select + ()) everytime you need a Select() followed by a ToArray() call

Xerillio
  • 4,855
  • 1
  • 17
  • 28