Question

Ok, back to basics. I am wondering how to correctly overload a method with a params argument.

Here's my scenario. I start with my regular method:

public void MyMethod(MyObject mo)
{
    // method body
}

And I created an overload for it that looks like this:

public void MyMethod(MyObject mo, params string[] fields)
{
    // new method body

    MyMethod(mo);
}

The obvious intention is for MyMethod(new MyObject()); to execute the original method and MyMethod(new MyObject(), "field0"/*, etc...*/); to execute the overloaded method. But that isn't what I'm finding to be the case.

What actually happens is that MyMethod(new MyObject()); executes the overloaded method!

I don't understand that. In this type of scenario, how would I execute the original method?

UPDATE with actual code

Okay, so here is the actual code that produces the described behavior.

Class1Base.cs:

public class Class1Base
{
    public virtual void MyMethod(MyObject ob)
    {
        Console.WriteLine("Called Class1Base");
    }
}

Class1.cs:

public class Class1 : Class1Base
{
    public override void MyMethod(MyObject ob)
    {
        Console.WriteLine("called overridden method");
    }

    public void MyMethod(MyObject ob, params string[] fields)
    {
        Console.WriteLine("called OVERLOADED method");
    }
}

MyObject.cs:

public class MyObject
{
    public int Id { get; set; }
    public string Description { get; set; }
}

Then, when I execute this code in this fashion:

var myClass = new Class1();
var myObject = new MyObject();
myClass.MyMethod(myObject);
myClass.MyMethod(null);
myClass.MyMethod(null, "string");

The console shows:

called OVERLOADED method
called OVERLOADED method
called OVERLOADED method

I would have expected it to show:

called overridden method
called overridden method
called OVERLOADED method

Why doesn't it?

Was it helpful?

Solution

I don't think you're telling us the whole story. Section 7.3.5.2 of the C# 5 spec (titled "Better function member") says in part:

• Otherwise, if MP is applicable in its normal form and MQ has a params array and is applicable only in its expanded form, then MP is better than MQ.

That seems to be the case here, since the params version needs to be "expanded" to a zero-length array. And in fact, trying your code locally produces the expected result of calling the non-params version.

Update: In response to your edit, the answer is now clear: You are calling the methods from Class1, which means that when performing overload resolution, methods marked as override are not considered initially. And since a non-overridden method is applicable (albeit in its expanded form,) that is the method chosen.

Specifically, section 7.6.5.1 read in part:

• The set of candidate methods is reduced to contain only methods from the most derived types: For each method C.F in the set, where C is the type in which the method F is declared, all methods declared in a base type of C are removed from the set.

The base class MyMethod() is excluded from the candidate set, and so it won't be selected by the algorithm.


The precise reasoning behind this behavior is to avoid a manifestation of the "brittle base-class" problem. Suppose we had the following class hierarchy:

class A
{
}

class B : A
{
    public void MyMethod(object o) { }
}

And the following call-site:

new B().MyMethod("a string");

That will obviously resolve to the MyMethod() that takes an object. But now suppose the creator of A (perhaps who works on another team) decides A should have a MyMethod(), too. So they change their class:

class A
{
    public void MyMethod(string s);
}

And now imagine what would happen if we didn't exclude methods from base types. Your call that was originally resolving to B.MyMethod() all of a sudden would resolve to A.MyMethod() (since the string is a better match.) The designers of C# didn't want to allow another person on a totally different team to silently change the meaning of your code.

For more information on brittle base-class issues, Eric Lippert's (old) blog has a number of posts on the topic. Just search around a bit.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top