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?