strange overloading behaviour

  • Thread starter Thread starter Karahan Celikel
  • Start date Start date
K

Karahan Celikel

Here are three simple classes:

class A
{
public void DoIt(B b)
{
DoSomething(b);
}
public void DoSomething(B b)
{
}
public void DoSomething(C c)
{
}
}

class B
{
}

class C : B
{
}

Here is some simple code with line numbers:

1. A a = new A();
2. C c = new C();
3. a.DoSomething(c);
4. a.DoIt(c);

Here is my question:

Line 3 will cause the overloaded method A.DoSomething(C c) to be called, but
line 4 causes A.DoSomething(B b) to be called. Why? That seems pretty wack
if you ask me. Yes, I can see that the first method to be called is DoIt(B
b), which then apparently causes the "wrong" subsequent method to be called.
But what is the underlying rationale for such behavior?
 
Here is my question:

Line 3 will cause the overloaded method A.DoSomething(C c) to be called, but
line 4 causes A.DoSomething(B b) to be called. Why? That seems pretty wack
if you ask me. Yes, I can see that the first method to be called is DoIt(B
b), which then apparently causes the "wrong" subsequent method to be called.
But what is the underlying rationale for such behavior?

Because otherwise things would be hard to predict at compile time. The
method *signature* is chosen at compile time, and the actual method
invoked depends on overriding (not overloading).

This is certainly the way Java works as well, and I suspect it's how
C++ works (although I haven't checked).
 
Karahan said:
Line 3 will cause the overloaded method A.DoSomething(C c) to be
called, but line 4 causes A.DoSomething(B b) to be called. Why? That
seems pretty wack if you ask me. Yes, I can see that the first
method to be called is DoIt(B b), which then apparently causes the
"wrong" subsequent method to be called. But what is the underlying
rationale for such behavior?

Overloaded methods get called based upon the type and/or number of
arguments passed to them. DoIt expects an object of type B. A C object
is a B object so you can pass a C to DoIt. However, DoIt will cast that
object to an instance of the base class B. When DoIt calls DoSomething,
you get DoSomething(B b) not DoSomething(C c).

Where you call DoSomething directly, the same rules are followed.
However, since you're not casting your C object to an instance of its
base, the correct method is called and DoSomething(C c) gets executed.

--
There are 10 kinds of people. Those who understand binary and those who
don't.

http://code.acadx.com
(Pull the pin to reply)
 
It is a drawback of C#.

Because overloaded method calls are determined at compile time rather than
runtime.

You ought to be able to designate overloaded methods as being "virtual" much
like overriden methods--so that the expected method is called.
 
I agree with you that it is somewhat confusing. Case in
point: What behavior would you expect b.GetType() to
exhibit inside the DoIt method? It will return "C",
not "B".

However, the behavior you've described is what I'd expect
to occur, but that may only be because my vision is
skewed from modeling classes that way.

JER
 
Steve M said:
It is a drawback of C#.

Because overloaded method calls are determined at compile time rather than
runtime.

You ought to be able to designate overloaded methods as being "virtual" much
like overriden methods--so that the expected method is called.

I don't see it as a drawback - I see it as *mostly* an advantage:

o The method being known ahead of time makes for much faster code
o The code is easier to follow because you can tell from reading the
source which method will be called (admittedly with polymorphism)
o The language is simpler (describing which method would be used
with multiple overloads and multiple overrides would be a nightmare;
it's bad enough as it is when it comes to overloading)
o There's no room for ambiguity at runtime.

The last point is an important one, IMO: you could easily end up with
an actual parameter which satisfies two or more methods equally well
(eg if the formal parameter types are unrelated interfaces, and you
happen to get a reference to an object of a type which implements both
interfaces) - what would you do then? A program which has compiled
successfully shouldn't have to give up at runtime because it can't find
the right method!

I'm glad you're at least suggesting it to be optional, but I haven't
seen many times where I'd use this, and keeping the language simple and
easy to follow is a big plus in my view.
 
I don't see it as a drawback - I see it as *mostly* an advantage:


Sounds like, "It's not a bug, it's a feature!"

o The method being known ahead of time makes for much faster code


Then let's throw out the "virtual" keyword.

o The code is easier to follow because you can tell from reading the
source which method will be called (admittedly with polymorphism)


If we had any doubts before, surely now we should throw out "virtual".

o The language is simpler (describing which method would be used
with multiple overloads and multiple overrides would be a nightmare;
it's bad enough as it is when it comes to overloading)


You've convinced me to remove all overrides from my applications.

o There's no room for ambiguity at runtime.


Not anymore. Inheritance has officially been banned in my shop!
Long live C, Pascal, and COBOL!
 
Steve M said:
Sounds like, "It's not a bug, it's a feature!"

Indeed. It's behaving the way it was designed to, and I'm glad it was
designed the way it was.
Then let's throw out the "virtual" keyword.

Speed was only one reason: I don't believe its utility is worth the
performance penalty, whereas the balance with virtual is entirely the
other way round.
If we had any doubts before, surely now we should throw out "virtual".

There's only one "dimension" in polymorphism - you don't have to worry
about the types of multiple things as you do with parameters, there's
only the type of the object being operated on.
You've convinced me to remove all overrides from my applications.

That's only because you appear to have decided not to really consider
my argument carefully.
Not anymore. Inheritance has officially been banned in my shop!
Long live C, Pascal, and COBOL!

Where does polymorphism give room for ambiguity at runtime?
 
Where does polymorphism give room for ambiguity at runtime?

Actually, it doesn't. And I don't believe my suggestion for a more dynamic
overloading mechanism does either. If the compiler will balk at ambiguity in
its currently implementation, it surely could do so in a dynamic one as
well.
 
Steve M said:
Actually, it doesn't. And I don't believe my suggestion for a more dynamic
overloading mechanism does either. If the compiler will balk at ambiguity in
its currently implementation, it surely could do so in a dynamic one as
well.

How, without loss of utility and at the expense of simplicity?
Consider:

interface IFoo
{
}

interface IBar
{
}

class X : IFoo, IBar
{
}

class Y
{
void Something(object o)
{
Overloaded(o);
}

void Overloaded(object o)
{
}

void Overloaded(IFoo x)
{
}

void Overloaded(IBar y)
{
}
}

No problems at compile-time here - Overloaded(object o) is called by
Something whatever the actual type of the object referred to by the
value of o is. Now consider what would happen in the system you're
suggesting, with dynamic overloading. What happens when you pass in a
reference to an instance of X? Both Overloaded(IFoo x) and
Overloaded(IBar y) are better than Overloaded(Object o), but neither is
better than each other - so you have ambiguity. This can be seen at
compile-time in the current system if you try to change the parameter
of Something to X x instead of object o.

Now, the compiler could catch the potential ambiguity here, but that
makes the language even *more* complicated - and still for little gain.
It would also limit the usefulness of the virtual overloading in the
first place, as you'd never be able to specify disparate interfaces (or
a class and an interface that class doesn't implement) as overloaded
parameters for two different methods.

I stand by my view that the "cost" of this system in terms of
simplicity and efficiency outweighs the utility.
 
Back
Top