C#: Passing Null to Overloaded Method - Which Method Is Called

C#: Passing null to overloaded method - which method is called?

It depends on TypeA and TypeB.

  • If exactly one of them is applicable (e.g. there is no conversion from null to TypeB because it's a value type but TypeA is a reference type) then the call will be made to the applicable one.
  • Otherwise it depends on the relationship between TypeA and TypeB.

    • If there is an implicit conversion from TypeA to TypeB but no implicit conversion from TypeB to TypeA then the overload using TypeA will be used.
    • If there is an implicit conversion from TypeB to TypeA but no implicit conversion from TypeA to TypeB then the overload using TypeB will be used.
    • Otherwise, the call is ambiguous and will fail to compile.

See section 7.4.3.4 of the C# 3.0 spec for the detailed rules.

Here's an example of it not being ambiguous. Here TypeB derives from TypeA, which means there's an implicit conversion from TypeB to TypeA, but not vice versa. Thus the overload using TypeB is used:

using System;

class TypeA {}
class TypeB : TypeA {}

class Program
{
static void Foo(TypeA x)
{
Console.WriteLine("Foo(TypeA)");
}

static void Foo(TypeB x)
{
Console.WriteLine("Foo(TypeB)");
}

static void Main()
{
Foo(null); // Prints Foo(TypeB)
}
}

In general, even in the face of an otherwise-ambiguous call, to ensure that a particular overload is used, just cast:

Foo((TypeA) null);

or

Foo((TypeB) null);

Note that if this involves inheritance in the declaring classes (i.e. one class is overloading a method declared by its base class) you're into a whole other problem, and you need to cast the target of the method rather than the argument.

Passing null value to overloading method where Object and String as param in C#

The C# compiler takes the most specific overload possible.

As string is an object, and it can have the value of null, the compiler deems string to be more specific.

Method overloading and null value

It is a product of overload resolution. Your argument, null, is convertible to both object and int[]. The compiler therefore picks the most specific version, because int[] is more specific than object.

Overloading null ambiguity

The problem is that both string and object are nullable, so null could refer to either overload of the method. You have to cast the null value—as stupid as that sounds—to say explicitely which overload you want to call.

method("string", (string) null);
method("string", (object) null);

This is basically the same as if you defined a variable of either type and passed that then:

string param1 = null;
object param2 = null;

method("string", param1); // will call the string overload
method("string", param2); // will call the object overload

Both param1 and param2 have the same value, null, but the variables are of different types which is why the compiler is able to tell exactly which overload it needs to use. The solution above with the explicit cast is just the same; it annotates a type to the null value which is then used to infer the correct overload—just without having to declare a variable.

Different overload method is chosen, depending from where it's called with a null parameter

Which overload to call (binding) is statically fixed for each invocation expression at compile-time (unless you use type dynamic at compile-time). Just because the expression you use for argument happens to evaluate to another type when the program runs, the overload will not magically change.

Examples:

FormatStuff.Format(null);

The compile-time type does not exist (null), but since there is an implicit conversion from the null literal to object and an implicit conversion from null to IEnumerable<object> as well, both overloads are candidates. In that case the overload with IEnumerable<object> is preferred because it is more specific.

FormatStuff.Format((object)null);

In this case the compile-time type of the expression is object, so only one overload applies, and that is used.

IEnumerable<int> intnumerable
// ...
FormatStuff.Format(intnumerable);

In the above case the compile-time type of what you pass is IEnumerable<int>. Here int is a value-type. An IEnumerable<int> is not an IEnumerable<object> at compile-time. This is fixed at compile-time; it does not matter whether intnumerable happens to be null at run-time, and if non-null, it does not matter what the actual type (some concrete class or struct implementing IEnumerable<int>) is at run-time.

IEnumerable<string> strEnumerable
// ...
FormatStuff.Format(strEnumerable);

Finally, in this case, since string is a reference type, the compile-time covariance of IEnumerable<out T> applies. So an IEnumerable<string> is an IEnumerable<object>. Therefore both overloads apply, and the most specific one is preferred.

CLR question. Why method overloading in C# decides that null is a string?

The answer is because it must choose a reference type (null doesn't work for value types), and every string is an object, but not every object is a string. See Jon Skeet's answer to this question for more information.

In response to your second example, if a variable that is null is passed to is, it will always evaluate to false, no matter what.

C# pass a null value as a generic object rather than a type for overloaded methods

You can force the behavior by casting null to anything other than LegacyData.

var x = new Test();
x.PerformCheck((object)null);

public class Test
{
public void PerformCheck(LegacyData data) { Console.WriteLine("legacy"); }
public void PerformCheck(object data) { Console.WriteLine("other"); }
}

public class LegacyData {}

This outputs "other" as expected.

How to overload Null-conditional operators ?.

No, you can't overload the Null-conditional operators. See the list of C# Overloadable operators.


Addendum The ability to overload this operator has actually been proposed to the C# language team. See Proposal: Allow null conditional (?.) and null coalescing ()?? operators to be overloaded and Proposal: nullable-like types. These has not been aproved.

What follows is my understanding of the concerns regarding these and similar proposals:

Changing the semantics of these operators could have a lot of ramifications. For example, given that the operators are static, it would be possible to make it say that something that is null is not. Which would mean a lot of problems for a lot of code. On the flip side, you could have code that hangs too long on a reference or not long enough that would bring problems with the garbage collection.

Even if these and similar issues could be solved, it is not a change to be taken lightly. We are talking about a lot of problems for existing code, that is already deployed in production.



Related Topics



Leave a reply



Submit