OK, so let's say that we abandon the other question where you incorrectly believe that any of this is a compiler bug and actually address your real question.
First off, let's try to state the real question. Here's my shot at it:
The preamble:
A "variadic" method is a method which takes an unspecified-ahead-of-time number of parameters.
The standard way to implement variadic methods in C# is:
void M(T1 t1, T2 t2, params P[] p)
that is, zero or more required parameters followed by an array marked as "params".
When calling such a method, the method is either applicable in its normal form (without params) or its expanded form (with params). That is, a call to
void M(params object[] x){}
of the form
M(1, 2, 3)
is generated as
M(new object[] { 1, 2, 3 });
because it is applicable only in its expanded form. But a call
M(new object[] { 4, 5, 6 });
is generated as
M(new object[] { 4, 5, 6 });
and not
M(new object[] { new object[] { 4, 5, 6 } });
because it is applicable in its normal form.
C# supports unsafe array covariance on arrays of reference type elements. That is, a string[]
may be implicitly converted to object[]
even though attempting to change the first element of such an array to a non-string will produce a runtime error.
The question:
I wish to make a call of the form:
M(new string[] { "hello" });
and have this act like the method was applicable only in expanded form:
M(new object[] { new string[] { "hello" }});
and not the normal form:
M((object[])(new string[] { "hello" }));
Is there a way in C# to implement variadic methods that does not fall victim to the combination of unsafe array covariance and methods being applicable preferentially in their normal form?
The Answer
Yes, there is a way, but you're not going to like it. You are better off making the method non-variadic if you intend to be passing single arrays to it.
The Microsoft implementation of C# supports an undocumented extension that allows for C-style variadic methods that do not use params arrays. This mechanism is not intended for general use and is included only for the CLR team and others authoring interop libraries so that they can write interop code that bridges between C# and languages that expect C-style variadic methods. I strongly recommend against attempting to do so yourself.
The mechanism for doing so involves using the undocumented __arglist
keyword. A basic sketch is:
public static void M(__arglist)
{
var argumentIterator = new ArgIterator(__arglist);
object argument = TypedReference.ToObject(argumentIterator.GetNextArg());
You can use the methods of the argument iterator to walk over the argument structure and obtain all the arguments. And you can use the super-magical typed reference object to obtain the types of the arguments. It is even possible using this technique to pass references to variables as arguments, but again I do not recommend doing so.
What is particularly awful about this technique is that the caller is required to then say:
M(__arglist(new string[] { "hello" }));
which frankly looks pretty gross in C#. Now you see why you are better off simply abandoning variadic methods entirely; just make the caller pass an array and be done with it.
Again, my advice is (1) under no circumstances should you attempt to use these undocumented extensions to the C# language that are intended as conveniences for the CLR implementation team and interop library authors, and (2) you should simply abandon variadic methods; they do not appear to be a good match for your problem space. Do not fight against the tool; choose a different tool.