3

I have a class with an overloaded Format method.

 class FormatStuff
 {
    public static string Format(object arg)
        => HandleObjectStuff();

    public static string Format(IEnumerable<object> args)
        => HandleListStuff();
 }

Now, when I call

FormatStuff.Format(null);

I end up in the second overload with the IEnumerable parameter. But in my case, I call the method from within a function like this:

 public static string DoStuff(IEnumerable<int> intnumerable)
 {
     StringBuilder sb = new StringBuilder();
     sb.Append(FormatStuff.Format(intnumerable));
     return sb.ToString();
 }

When I call this function like

DoStuff(null);

I end up in the first overload with the single object parameter, even though in both cases null is passed as the parameter.

Why is this and what can I do to end up in the second overload that matches the type of the DoStuff-parameter?

Edit: The question has been marked as a possible duplicate of this one. I don't think that's entirely the case, because the salient point that helped me understand my problem was, that an IEnumerable<int> is not an IEnumerable<object>. In general that means, that one cannot expect an IEnumerable of any type to be an IEnumerable of object, which I did not know. This conclusion is not drawn in the mentioned post.

  • 3
    An `IEnumerable` isn't an `IEnumerable` so that overload doesn't apply. – Lee Feb 13 '17 at 12:36
  • Then why does it take the IEnumerable overload when I call the Format function directly with `FormatStuff.Format(null);`? – Leon Schreiber Feb 13 '17 at 12:43
  • `null` is an `IEnumerable` and that is 'more specific' than `object` so that overload is chosen. If you call `Format` with an `IEnumerable` only the `object` overload applies. See [this answer](http://stackoverflow.com/a/5174773/152602) which describes how overloads are resolved. – Lee Feb 13 '17 at 12:44
  • Possible duplicate of [Overloading null ambiguity](http://stackoverflow.com/questions/21392957/overloading-null-ambiguity) – Sefe Feb 13 '17 at 13:03

2 Answers2

2

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.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
0

To achieve what you want, your method must be able to distinguish between the two method calls. When you pass null the Format() method doesn't know if your null is an object or IEnumerable<object> since both are of type object. To solve your issue you can do one of the following:

  • 1 Change the second method as Format(IEnumerable<int> args)

OR

  • 2 Change the type signature of your method by adding optional arguments. Take this as an example
Community
  • 1
  • 1
sziraqui
  • 5,763
  • 3
  • 28
  • 37