4

Given the following extension method:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}

I can write:

var foo = new[] { "cd", "ef", }.Prepend("ab");

which yields the desired:

{ "ab", "cd", "ef", }

Great, but I can also write:

var bar = new[] { "cd", "ef", }.Prepend();

which is totally nonsensical, yet still valid, code. I want to prevent such abuse of my methods.

I could rewrite the method to the following:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, T second, params T[] third)
{
    return new[] { second, }.Concat(third).Concat(first);
}

but then I have to decide in the body as to whether I want to combine second and third and then .Concat them, or whether I want to do multiple .Concat calls. Either way there's an array creation and extra method call involved, which isn't optimal.

This answer suggests a simple and novel way to get around the problem - declare a no-args method overload and mark it as Obsolete so that it generates a compile error:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}

[Obsolete("Don't use this method, it always throws.", true)]
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first)
{
    throw new InvalidOperationException();
}

Now using the method with no arguments picks the concrete overload and fails when I try to compile - VS2015 even gives me IntelliSense to tell me that I'm Doing It Wrong™:

// compiler says yes
var foo = new[] { "cd", "ef", }.Prepend("ab");
// compiler says NO
var bar = new[] { "cd", "ef", }.Prepend();

The problem is, this feels like a hack. I'd much rather be able to write something like the following:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, [MinLength(1)] params T[] second)
{
    return second.Concat(first);
}

which is much more intuitive. Unfortunately, out-of-the-box attributes in C# don't provide compile-time validation, and I don't want to pull in something like PostSharp or LinFu for such a simple use case.

So my question: is there any way to achieve this in default, bog standard, C# 6?

Community
  • 1
  • 1
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
  • 8
    Imagine that, in some circumstances, the caller may not know ahead of time whether they're going to generate an empty array. Your code can easily deal with such an argument, with no extra code, but instead you want to force the caller to have explicit code that checks for such circumstances and changes its behaviour. Not very friendly. – Damien_The_Unbeliever Feb 01 '16 at 10:57

2 Answers2

4

One option would be to add a Roslyn analyzer - this allows you to generate arbitrary compiler errors.

Another option would be to manually add the first argument:

public static IEnumerable<T> Prepend<T>
  (this IEnumerable<T> @this, T first, params T[] rest)

But this nicely shows the reason why this is a terrible idea - it means that when you call the method with dynamic arguments, you have to manually split the array to first argument and the rest. Do you really want to force all the users of your method to check if the array they are passing isn't empty? Why? Your method can easily handle both cases. Do you really think the arbitrary limitation is worth it? No other LINQ-style method works that way - you'll likely cause more trouble than you're solving (and in my opinion, you're not solving any problem).

Luaan
  • 62,244
  • 7
  • 97
  • 116
0

There is no better solution available at this time than tools that support AOP (aspect oriented programming), like PostSharp. I would go for that option.

Another option might be to pull this out of the code base and create a Roslyn code analyzer to do the 'quality' checks for you. This is not the ideal in my opinion, but it might be an option if you don't want to use PostSharp.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325