Wednesday, June 10, 2009

 

Dangers of Overloading Methods in Derived Classes

Like most languages, C# supports method overloading. Basically, that simply means that you are allowed to have multiple methods with the same name as long as the prototypes (method signatures) are different. In theory, the compiler chooses the "best" method based on the parameters passed into the method. And, it generally does a good job. But, if the overloaded methods are distributed across several classes derived from one another, the compiler may not always choose the method you expect!

Consider the example below.



public class BaseClass1
{
public int MyMethod(int x)
{
return x - 1;
}
}

public class DerivedClass1 : BaseClass1
{
public int MyMethod(object obj)
{
return (int)obj + 1;
}
}

DerivedClass1 dc1 = new DerivedClass1();
int x1 = 100;
int y1 = dc1.MyMethod(x1); // y1 == 101

 

Given the example above, you might expect y1 to be 99 because MyMethod(int) is a better match than MyMethod(obj). But, if that is what you thought then you would be wrong. The reality is that y1 == 101 because the compiler chooses MyMethod(obj) over MyMethod(int) from the base class.

The same thing applies if the method is virtual:



public class BaseClass2
{
public virtual int MyMethod(int x)
{
return x - 1;
}
}

public class DerivedClass2 : BaseClass2
{
public virtual int MyMethod(object obj)
{
return (int)obj + 1;
}
}

DerivedClass2 dc2 = new DerivedClass2();
int x2 = 100;
int y2 = dc2.MyMethod(x2); // y2 == 101

 

In the case of virtual methods above, it seems like you could override the MyMethod(int) from the base class to fix the problem. But, if that's what you think, then you would be wrong again.



public class BaseClass3
{
public virtual int MyMethod(int x)
{
return x - 1;
}
}

public class DerivedClass3 : BaseClass3
{
public override int MyMethod(int x)
{
return base.MyMethod(x);
}

public virtual int MyMethod(object obj)
{
return (int)obj + 1;
}
}

DerivedClass3 dc3 = new DerivedClass3();
int x3 = 100;
int y3 = dc3.MyMethod(x3); // y3 == 101

 

The same applies if the method is static. Although, in the case of static methods you might have expected that because you are specifically scoping to the class name. But, on the other had, public static methods of a base class are also available as public static methods on the derived class, so you might still have expected the compiler to choose MyMethod(int) over MyMethod(object).



public class BaseClass4
{
public static int MyMethod(int x)
{
return x - 1;
}
}

public class DerivedClass4 : BaseClass4
{
public static int MyMethod(object obj)
{
return (int)obj + 1;
}
}

int x4 = 100;
int y4 = DerivedClass4.MyMethod(x4); // y4 == 101

 

I have not really found a good solution to this problem. Luckily, it does not occur often (for me) because I generally do not distribute overloaded methods across multiple class. But, if I do need to do something like that, the only solution I have found is really just a workaround: use the ref keyword to help the compiler pick the right method; since the compiler knows it has to pass the parameter both in and out, it tends to do the method selection better. Although, I would hate to have to do that because it introduces a "new danger" of allowing the method to change the value.



public class BaseClass5
{
public virtual int MyMethod(ref int x)
{
return x - 1;
}
}

public class DerivedClass5 : BaseClass5
{
public virtual int MyMethod(ref object obj)
{
return (int)obj + 1;
}
}

DerivedClass5 dc5 = new DerivedClass5();
int x5 = 100;
int y5 = dc5.MyMethod(ref x5); // y5 == 99

 

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]