Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
762 views
in Technique[技术] by (71.8m points)

arrays - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 })?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...