C #: Why does null passing accept overload using Object [] (but only in some cases)?

Uncommenting the marked line below will result in a stackoverflow transition, since overload resolution favors the second method. But inside the loop in the second method, the code path takes the first overload.

What's going on here?

private static void Main(string[] args) { var items = new Object[] { null }; Test("test", items); Console.ReadKey(true); } public static void Test(String name, Object val) { Console.WriteLine(1); } public static void Test(String name, Object[] val) { Console.WriteLine(2); // Test(name, null); // uncommenting this line will cause a stackoverflow foreach (var v in val) { Test(name, v); } } 
+7
c #
source share
3 answers

The second call works as you expect, because val in the second method is of type Object[] , so in foreach , var v type of Object is easily inferred. There is no ambiguity.

The second call is ambiguous: Object and Object[] references may be null , so the compiler must guess what you mean (more on this below). null does not have its own type; if that were the case, you would need an explicit cast to do anything with it that would be unpleasant.

Overload resolution occurs at compile time, and not at run time. Overload resolution in a loop is independent of whether v sometimes null ; which will not be known before execution, long before the overload is resolved by the compiler. It is based on declared type v . The declared type v is inferred, not explicitly declared, but the fact is that it is known at compile time, when overloads are allowed.

In another call, in which you explicitly pass null , the compiler should deduce which overload you want to use with the algorithm ( here is the answer in a language that normal people can hope to understand ), which in this case comes with the wrong answer.

Of the two, he selects Object[] , because Object[] can be dropped to Object , but the opposite is not true - Object[] is "more specific" or more specialized. This is further from the root of the type hierarchy (or in plain English, in this case one of the Object types and the other not).

Why is specialization a criterion? The assumption is that for two methods with the same name it is assumed that the general type of the argument is common (you can impose something on Object ), and overloading with the type further towards the sheets the hierarchy type will be used to replace the method general case for certain specific cases: "Stick to this for everything, if it's not an array from Object , I need to do something else for arrays of objects."

This is not the only conceivable criterion, but I cannot think of any other half as good.

In this case, it looks contradictory, because you think of null as general, as something that could be: it is not even specifically Object . This is anything.

The following actions would work because here the compiler should not guess what you mean by null :

 public static void Test(String name, Object[] val) { Console.WriteLine(2); Object dummy = null; Test(name, dummy); foreach (var v in val) { Test(name, v); } } 

Short answer: Explicit nulls makes overload resolution a mess, to such an extent that sometimes I wonder if it might be a mistake on the part of the language developers to even try to figure them out (NB "Sometimes I wonder if it can ..." not is an expression of dogmatic confidence, the guys who developed the language are smarter than me).

The compiler is as smart as possible that itโ€™s not very. He may have sporadic bouts of outright malice, but in this case, just good intentions went wrong.

+4
source share

When overloaded, if ambiguity is encountered, the compiler will always try to execute the most specific method.

In this case, object[] more specific than object .

null can be any type, so it matches both method signatures. Since the compiler must decide, it will choose Test(string name, Object[] val) , which raises a StackOverflowException .

Inside your foreach loop , however, v assumed to be of type object . Note that you now have a typed variable.

Like object , v can be either object or object[] (or virtually any type), but the compiler does not know that, at least, it will not know until execution.

Overloading is allowed at compile time, so the only key the compiler has is that v is an object , so it will select Test(string name, Object value);

If you had the following line:

 var val = new object[] { }; Test(name, val); 

Then Test(string name, Object[] val) would be called, since the compiler knows that val is object[] at compile time.

+3
source share

In his appeal to Test(name, v); your variable v as the type of Object associated with it, even if it is null , we still know "what kind of zero it is."

In your call to Test(name, null); this null has no type associated with it, so the compiler finds the closest match and uses this overload. The closest match is overloading object[] .

+1
source share

All Articles