C# params object[] strange behavior

Considering this code

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] strings = new string[] { "Test1", "Test2", "Test3" };

            int[] ints = new int[] { 1, 2, 3, 4 };

            Test(strings);

            Test(ints);
        }

        public static void Test(params object[] objects)
        {
        }
    }
}

And this page https://msdn.microsoft.com/fr-ca/library/w5zay9db.aspx

I would expect (params object[] objects) to be an array of one element with a string[] as the first element, but when I debug, I see that (params object[] objects) is { "Test1", "Test2", "Test3" }.

However, with an int[], I get an object[] with int[] as first element.

With strings

With ints

Is this undefined behavior? Does that depend on the .Net framework version / Mono version?


Nice find!

Is this undefined behavior?

No. This is by-design behaviour. Weird design, but by design.

Does that depend on the .Net framework version / Mono version?

No. All versions of C# have this behaviour.

This is a consequence of the collision of some interesting rules of C#.

The first relevant rule is: a method with a params array may be called in either "normal" or "expanded" form. Normal form is as if there was no "params". Expanded form takes the params and bundles them up into an array that is automatically generated. If both forms are applicable then normal form wins over expanded form.

Now, that perhaps seems sensible; if you have an array of objects in hand, odds are good that you want to pass the array of objects, not an array that contains an array of objects.

The second relevant rule is that C# allows unsafe array covariance when the element type is a reference type. That is, an array of strings may be converted to an array of objects implicitly. You'll note that this has two implications. First, it means that when you have an array of objects, it might actually be an array of strings, so putting, say, a turtle into that array of objects might cause a type error. This is very surprising! You expect that every array of objects can take any object, but that is not true in C#. Some arrays of objects are lying.

The second implication is: since putting that turtle into what is really an array of strings must throw, it means that every time you put something into an array of base type, the runtime must verify that the types check. So array writes are extra expensive in C# on every write, so that the vanishingly small minority of bad writes can be caught.

This is a mess, and this is why unsafe array covariance tops my list of unfortunate C# features.

The combination of these two rules explains your observations. The array of strings is convertible to an array of objects, and therefore the method is applicable in normal form.

For the array of ints, well, covariance does not apply to value types. So the array of ints is not convertible to an array of objects, so the method is not applicable in its normal form. But an array of ints is an object, so it is applicable in expanded form.

See also:

Why does params behave like this?

Your question is arguably a duplicate of:

Is there a way to distingish myFunc(1, 2, 3) from myFunc(new int[] { 1, 2, 3 })?


Im not an expert but the params keyword idea was to enable the posibility to make different calls to the method independent of how many elements you have.

Test(object1)
Test(object1, object2)
Test(object1,..., objectN)

So what you are seeing is the normal behaviour nothing weird. More info on this link msdn

By using the params keyword, you can specify a method parameter that takes a variable number of arguments.

You can send a comma-separated list of arguments of the type specified in the parameter declaration or an array of arguments of the specified type. You also can send no arguments. If you send no arguments, the length of the params list is zero.

No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration.