473,422 Members | 2,224 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,422 software developers and data experts.

C# Override limitation..why?

Why can't I do this in C#

public class A
{
public A virtual whatever( A a )
{
}
}

public class B : A
{
public B override whatever( B b )
{
}
}

Why does the signature of the override have to match exactly the base
version?

It seems like a perfectly safe thing to do so why can't I do it? In
some situations it saves lots of casting with duplicate methods.

Cliff

Nov 17 '05 #1
15 3761
<Cl**********@hotmail.com> schrieb im Newsbeitrag
news:11**********************@g43g2000cwa.googlegr oups.com...
Why can't I do this in C#

public class A
{
public A virtual whatever( A a )
{
}
}

public class B : A
{
public B override whatever( B b )
{
}
}

Why does the signature of the override have to match exactly the base
version?

It seems like a perfectly safe thing to do so why can't I do it? In
some situations it saves lots of casting with duplicate methods.

Cliff


Hi Cliff,

consider following code:
A a = new B();
a.whatever (new A());

the parameter b of the override method in B would not be of type B.
That's th problem.

Christof
Nov 17 '05 #2
how exactly is it a safe thing to do? consider

A a = new B();

what would the signature of a.whatever() be?

"Cl**********@hotmail.com" wrote:
Why can't I do this in C#

public class A
{
public A virtual whatever( A a )
{
}
}

public class B : A
{
public B override whatever( B b )
{
}
}

Why does the signature of the override have to match exactly the base
version?

It seems like a perfectly safe thing to do so why can't I do it? In
some situations it saves lots of casting with duplicate methods.

Cliff

Nov 17 '05 #3
Cliff,

As others have already pointed out it the compiler doesn't know what
signature to use in all cases. What you can do, however, is use
explicit interface implementation to get at least part of what you
want.

public interface A
{
A whatever(A a);
}

public class B : A
{
public B whatever(B b)
{
Console.WriteLine("B");
return b;
}

// Explicit Interface Implementation
A A.whatever(A a)
{
Console.WriteLine("A");
return a;
}
}

static void Main()
{
B b = new B();
A a = b;

//b.whatever(a); // compile error!
b.whatever(b);
a.whatever(a);
a.whatever(b);
}

The important thing note here is that B still needs A's whatever method
signature so that the right method is called when you're using A
reference types.

Brian

Cl**********@hotmail.com wrote:
Why can't I do this in C#

public class A
{
public A virtual whatever( A a )
{
}
}

public class B : A
{
public B override whatever( B b )
{
}
}

Why does the signature of the override have to match exactly the base
version?

It seems like a perfectly safe thing to do so why can't I do it? In
some situations it saves lots of casting with duplicate methods.

Cliff


Nov 17 '05 #4
I see what you are getting at but this is the "extra methods" and
casting I'm trying to avoid.

I'm trying to work out why, when I'm building layered code, I would
ever do

A a = new B();

either in the "A" layer (as it doesn't know what B is) or in the "B"
layer where I would create a "B" and then (maybe) pass it to a method
in the lower layer which obviously expects an A.

I simply have some base classes where I want to create virtual methods
which accept "something based on A" but the actual class implements a
type-specific (B) version of the method.

Nov 17 '05 #5
I'm not really suggesting the compiler/runtime does anything any
different than it does now.

OK, in your example you end up with

a.whatever(new A()) calling b.whatever(A) but whats the problem it can
still call a.whatever(A) though the normal virtual lookup tables (or
whatever it uses). It knows B is based on A and it knows its an
override so why can't it look for base.whatever(a) in the base class?

Why can't the compiler say

"OK it should be an A, but it's a B, but that's OK because B is based
on A" and just carry on creating the same code it does now as if I had
declared in identically.

I suppose all I'm saying is that when it finds "override" it should
look for methods in the base class where the signature parameters match
at "some base class level" and allow it. It then generates code as it
would have done had they been identical.

Perhaps I'm missing some fundamental point but it feels sensible that a
base method accepting a base class could be overridden by a superset
class and method, after all it contains an instance of the base class.

It also happens with

abstract whatever( BaseClass classA )

and you want to implement a specific version

override whatever( ChildClass classB ) // classB : classA

what I am trying to say with the abstract class is "implement a method
which accepts any parameter but it must be based on BaseClass".

There are many examples of this in class libraries where the override
method has to "cast" the override parameter and possibly call a second
type-specific implementation of the same method.

Cliff

Nov 17 '05 #6
"Cl**********@hotmail.com" wrote:
I see what you are getting at but this is the "extra methods" and
casting I'm trying to avoid.

I'm trying to work out why, when I'm building layered code, I would
ever do

A a = new B();

then you are completed missing the whole point of polymorphism in inheritance.
either in the "A" layer (as it doesn't know what B is) or in the "B"
layer where I would create a "B" and then (maybe) pass it to a method
in the lower layer which obviously expects an A.

I simply have some base classes where I want to create virtual methods
which accept "something based on A" but the actual class implements a
type-specific (B) version of the method.


what you really want to do is not inheritance, but more like what generics
(or template if you are familiar with C++) will offer for you in the next
version.
Nov 17 '05 #7
<Cl**********@hotmail.com> wrote in message
news:11*********************@z14g2000cwz.googlegro ups.com...
I see what you are getting at but this is the "extra methods" and
casting I'm trying to avoid.

I'm trying to work out why, when I'm building layered code, I would
ever do

A a = new B();
A a = new B(); is just an 'easy' example.
The value for the variable a could come from anywhere.
either in the "A" layer (as it doesn't know what B is) or in the "B"
layer where I would create a "B" and then (maybe) pass it to a method
in the lower layer which obviously expects an A.


The second one you mention there is a perfect example.
You call some method from the "B" layer - which, like you said, is creating
B's:

B b = new B();

someObject.SomeMethod(b);

So you have some method in that lower level that expects an A:
void SomeMethod(A someA)
{
someA.whatever(???)
}

does that whatever call expect an A or a B?
Nov 17 '05 #8
It achieves that "magic" at the moment because both classes implement
whatever(a) but it knows that someA is really a B so it calls
b.whatever(a).

You should never get b.whatever(a) called with a "A". Because it would
know the origin is A and calls a.whatever(a). b.whatever() is only ever
called with a "B" that is why it is always safe to cast it to a "B"
inside this overriden method.

I am right in thinking that B.whatever() gets called if I use the
correct override with the same signature? To do this the compiler
knows the origin of "someA" is really "B" and it looks this up in a
table and jumps to the correct method. Is that correct?

So, In my world :) the compiler uses the same logic.

It would still call B.whatever() because B implements

whatever( B b )

and it's OK because "B" is just a bigger "A". It's the big brother of

whatever( A a )

I think all I'm after is the syntax checker to say "ok I'm overriding
whatever() and the type I have is B. Has the base got a whatever(b)?
NO. Has it got a whatever(a parent of B)? YES." OK, so that's a valid
override, lets continue as usual and generate the same virtual lookup
tables as if he had declared it as whatever(A) in B. The vitual looukp
tables would be identical, anything derived from B would call
b.whatever and anything thats a simple A would call a.whatever().

Clear a mud to me :)

Cliff

Nov 17 '05 #9
<Cl**********@hotmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...
You should never get b.whatever(a) called with a "A". I think this statement may be the root of the problems :)
Because it would
know the origin is A and calls a.whatever(a). b.whatever() is only ever
called with a "B" that is why it is always safe to cast it to a "B"
inside this overriden method. That is a very specific statement to your application. The compiler has no
way of realizing you only intend for it to be called by B.
I am right in thinking that B.whatever() gets called if I use the
correct override with the same signature? To do this the compiler
knows the origin of "someA" is really "B" and it looks this up in a
table and jumps to the correct method. Is that correct?

NO :)
How can the compiler know that "the origin of someA is really B"? It has no
way to determine what type an object is at compile time (beyond the actual
variable declaration). That is something that must be determined at
RUNTIME.

ok - Lets say we have the following:

A a = ... some value
B b = new B();

Now, we do not know whether or not a is just an A, or is actually a B. We
do not know - and neither does the compiler!
Thus, when you call:
b.whatever(a);

The compiler can only assume that we have an A and thus calls whatever(A a)
accordingly.
OK - suggestion for you. Leave the declaration the same as in A (thus it is
a true override). If YOU know, that it will only be a B object, then you
can cast it yourself:

class B : A
{
public A override whatever( A a )
{
B b = a as B;
if (b != null)
{
//do whatever you wanted to do here with b.
}
else
{
return base.whatever(a);
}
}
}

The else is there in case your 'guarrantee' of being called only with a B is
ever violated - it would just turn and call A's implementation. This isn't
necessarily the best coding practice, but it should accomplish what you
want.
Nov 17 '05 #10
Cliff,
And what happens if I make a new class C also derived from A and also
having a whatever() method, and now I pass a C instead of an A or a B? The
compiler did not even know of the existence of C when you originally
compiled the caller of whatever. So your approach would require either
recompiling everything when a new class is derived from an old one, or the
compiler would need to do complete type lookups at every function call. The
idea of inheritence is to avoid both of those scenarios.
Bob
<Cl**********@hotmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...
It achieves that "magic" at the moment because both classes implement
whatever(a) but it knows that someA is really a B so it calls
b.whatever(a).

You should never get b.whatever(a) called with a "A". Because it would
know the origin is A and calls a.whatever(a). b.whatever() is only ever
called with a "B" that is why it is always safe to cast it to a "B"
inside this overriden method.

I am right in thinking that B.whatever() gets called if I use the
correct override with the same signature? To do this the compiler
knows the origin of "someA" is really "B" and it looks this up in a
table and jumps to the correct method. Is that correct?

So, In my world :) the compiler uses the same logic.

It would still call B.whatever() because B implements

whatever( B b )

and it's OK because "B" is just a bigger "A". It's the big brother of

whatever( A a )

I think all I'm after is the syntax checker to say "ok I'm overriding
whatever() and the type I have is B. Has the base got a whatever(b)?
NO. Has it got a whatever(a parent of B)? YES." OK, so that's a valid
override, lets continue as usual and generate the same virtual lookup
tables as if he had declared it as whatever(A) in B. The vitual looukp
tables would be identical, anything derived from B would call
b.whatever and anything thats a simple A would call a.whatever().

Clear a mud to me :)

Cliff

Nov 17 '05 #11
Hi, I couldnt see a reply like this one so hope I'm not repeating
anything.....

I've achieved a very similar thing in my code, I wanted an abstract
base class to reduce code duplicate across 3 sub-classes. However,
each of the 3 sub-classes also had their own instance of another
sub-class. This was separating business logic from presentation logic,
hence I came up with this ( you have to ensure the base classes are
abstract but other than that it has helped me a lot! ):

class BusinessBase
{
protected PresentationBase presentationInstance;

public PresentationBase UI
{
get{ return this.presentationInstance; }
set{ this.presentationInstance = value; }
}
}

class PresentationBase
{
....
}

class Class1PresentationBase : PresentationBase
{
....
}

class Class1Business : BusinessBase
{
new public Class1PresentationBase UI
{
get{ return (Class1PresentationBase)this.presentationInstance;
}
set{ this.presentationInstance = (Class1PresentationBase)value;
}
}
}

Nov 17 '05 #12
I think what I'm getting confused about is demonstrated by this, which
backs up what you said

public class A
{
public virtual int whatever( A a )
{
return 1;
}

public int fred(A a)
{
return whatever( a );
}
}

public class B : A
{
public override int whatever( A a )
{
return 2;
}
}
A aa = new A();
B bb = new B();

int z = aa.fred( aa ); // z = 1
int y = aa.fred( bb ); // y = 1
int x = bb.fred( aa ); // x = 2 -- backs up your statement (you could
not cast (B)a in b.whatever() safely)
int w = bb.fred( bb ); // w = 2

"fred" belongs in "A" but calls b.whatever() whenever we use "bb.fred".
Obviously this is no magic derived from the parameter type but the
class pointer "bb.".
From the code workarounds that have been posted many people have come

across this and have their own workaround (as do I) so it's a common
problem/pattern.

My original point was more simple, all I want is the compiler to
"ignore" what I have typed

public override int whatever( B b )

and assume I had typed

public override int whatever( A a )

and everything works as normal. In other words the compiler "always"
traces back to the "root" types of the parameters in the "override"
declaration and if it matches the type of the parameter in the
"virtual" declaration it's happy and replaces what I typed with the
type declared on the "virtual" declarion.

I can now see this would be confusing as the declaration would suggest
that you would always get a "B" when, as we have seen" it could be an
"A".

But...

As one person pointed out you can use "is" to workout if the parameter
is a "B" and cast accordingly so it occurs to me that the compiler
could do something along those lines also.

Could there be a clean way of implementing this in the language I
wonder.

As someone suggested the runtime would have to check parameter types at
runtime and call the appropriate method. So be it. If that's what I
want I take the hit. When the compiler discovers such a "special"
method (declared svirtual maybe) it creates a subroutine call and some
lookup table (populated in the constructor) so that when a call to such
a method is made it does an "is", and looks up the type and calls the
method that matches.

I won't hold my breath waiting for it to be implemented though!

It just seems a common scenario to me, I call methods "A(b)" from layer
"B" knowing that methods in "B" will be called with "b" coming through
and I always need to cast them which as we have seen is unsafe in some
situations. There must be a better way....

Cliff

Nov 17 '05 #13
Cliff,

What you are suggesting is too confusing to be apart of a language. In
fact, I'm still confused about what it is you are suggesting not to
mention how the compiler and runtime would handle it.

I contend that using the "is" or "as" operators are sufficient in all
cases because there is typically only one check to be made. That check
is usually to see if the parameter matches the type where the method is
declared. It might result in slightly more code, but it is
significantly easier to understand.

Brian

Cl**********@hotmail.com wrote:
[snip]

My original point was more simple, all I want is the compiler to
"ignore" what I have typed

public override int whatever( B b )

and assume I had typed

public override int whatever( A a )

and everything works as normal. In other words the compiler "always"
traces back to the "root" types of the parameters in the "override"
declaration and if it matches the type of the parameter in the
"virtual" declaration it's happy and replaces what I typed with the
type declared on the "virtual" declarion.

I can now see this would be confusing as the declaration would suggest
that you would always get a "B" when, as we have seen" it could be an
"A".

But...

As one person pointed out you can use "is" to workout if the parameter
is a "B" and cast accordingly so it occurs to me that the compiler
could do something along those lines also.

Could there be a clean way of implementing this in the language I
wonder.

As someone suggested the runtime would have to check parameter types at
runtime and call the appropriate method. So be it. If that's what I
want I take the hit. When the compiler discovers such a "special"
method (declared svirtual maybe) it creates a subroutine call and some
lookup table (populated in the constructor) so that when a call to such
a method is made it does an "is", and looks up the type and calls the
method that matches.

I won't hold my breath waiting for it to be implemented though!

It just seems a common scenario to me, I call methods "A(b)" from layer
"B" knowing that methods in "B" will be called with "b" coming through
and I always need to cast them which as we have seen is unsafe in some
situations. There must be a better way....

Cliff


Nov 17 '05 #14

<Cl**********@hotmail.com> wrote in message
news:11**********************@g47g2000cwa.googlegr oups.com...
I'm not really suggesting the compiler/runtime does anything any
different than it does now.

OK, in your example you end up with

a.whatever(new A()) calling b.whatever(A) but whats the problem it can
still call a.whatever(A) though the normal virtual lookup tables (or
whatever it uses). It knows B is based on A and it knows its an
override so why can't it look for base.whatever(a) in the base class?

Why can't the compiler say

"OK it should be an A, but it's a B, but that's OK because B is based
on A" and just carry on creating the same code it does now as if I had
declared in identically.

I suppose all I'm saying is that when it finds "override" it should
look for methods in the base class where the signature parameters match
at "some base class level" and allow it. It then generates code as it
would have done had they been identical.

Perhaps I'm missing some fundamental point but it feels sensible that a
base method accepting a base class could be overridden by a superset
class and method, after all it contains an instance of the base class.


Consider this, in your B class method you write

override int Method(B b)
{
b.SomeMethodThatIsNotInA();
}

How can you generate identical code for that? It's not that the compiler
can't treat a derived type as its base type, its that by doing so you defeat
the purpose of this feature to begin with. What good does it do, outside of
very strict type safety, to require type B in a spot where only type A's
interface can be used?

Best as I can tell, from all of the posts here, is that you want to make the
compiler *pretend* to override something but actually to generate method
overloads and then do dynamic dispatch based on actual type instead of
static dispatch based on expression type. To me, that is downright silly and
a waste of time(not to mention wholly different from the rest of the
langauge). If you want to dispatch to a method based on exact instance type,
write a reflection based dispatcher and be done with it(it'll take you 20
minutes, tops). If you want to provide overloads, do so, but don't expect
the compiler to do all the work for you.

What you seem to want doesn't make sense as a language feature directly and
can very easily be provided by a class.

What you are looking at here is called "covariant" arguments, and it has the
issues mentioned here(I don't know a language that supports it off hand, but
its not generally accepted, its exclusion was not simply arbitrary).
Contravariant arguments, something like:

virtual void Method(string x)
{
}

override void Method(object x)
{

}

are more commonly used and would work fine in C# as long as you don't make a
base call, but are not currently supported. Covariance does work for return
types, however:

virtual A Method()
{
}

override B Method()
{

}

although it is not currently supported either. Dig around and see what you
can find on covariant arugments, see if you think they are still a good idea
and if you can find an argument to really support them.
Nov 17 '05 #15

<Cl**********@hotmail.com> wrote in message
news:11**********************@g47g2000cwa.googlegr oups.com...
I'm not really suggesting the compiler/runtime does anything any
different than it does now.

OK, in your example you end up with

a.whatever(new A()) calling b.whatever(A) but whats the problem it can
still call a.whatever(A) though the normal virtual lookup tables (or
whatever it uses). It knows B is based on A and it knows its an
override so why can't it look for base.whatever(a) in the base class?

Why can't the compiler say

"OK it should be an A, but it's a B, but that's OK because B is based
on A" and just carry on creating the same code it does now as if I had
declared in identically.

I suppose all I'm saying is that when it finds "override" it should
look for methods in the base class where the signature parameters match
at "some base class level" and allow it. It then generates code as it
would have done had they been identical.

Perhaps I'm missing some fundamental point but it feels sensible that a
base method accepting a base class could be overridden by a superset
class and method, after all it contains an instance of the base class.


Consider this, in your B class method you write

override int Method(B b)
{
b.SomeMethodThatIsNotInA();
}

How can you generate identical code for that? It's not that the compiler
can't treat a derived type as its base type, its that by doing so you defeat
the purpose of this feature to begin with. What good does it do, outside of
very strict type safety, to require type B in a spot where only type A's
interface can be used?

Best as I can tell, from all of the posts here, is that you want to make the
compiler *pretend* to override something but actually to generate method
overloads and then do dynamic dispatch based on actual type instead of
static dispatch based on expression type. To me, that is downright silly and
a waste of time(not to mention wholly different from the rest of the
langauge). If you want to dispatch to a method based on exact instance type,
write a reflection based dispatcher and be done with it(it'll take you 20
minutes, tops). If you want to provide overloads, do so, but don't expect
the compiler to do all the work for you.

What you seem to want doesn't make sense as a language feature directly and
can very easily be provided by a class.

What you are looking at here is called "covariant" arguments, and it has the
issues mentioned here(I don't know a language that supports it off hand, but
its not generally accepted, its exclusion was not simply arbitrary).
Contravariant arguments, something like:

virtual void Method(string x)
{
}

override void Method(object x)
{

}

are more commonly used and would work fine in C# as long as you don't make a
base call, but are not currently supported. Covariance does work for return
types, however:

virtual A Method()
{
}

override B Method()
{

}

although it is not currently supported either. Dig around and see what you
can find on covariant arugments, see if you think they are still a good idea
and if you can find an argument to really support them.
Nov 17 '05 #16

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

15
by: Thomas Christmann | last post by:
Hi! I have a rather special question here. I'd like to write a wrapper for a .NET assembly, and register that on my server so that the people on my server call my assembly instead of the...
2
by: terpatwork | last post by:
Hi, (1) I have an access form that allows users to enter data, and when they click a button, the OnClick code that I've written uses a SQL INSERT statement to insert the data into the database. I...
3
by: Seong-Kook Shin | last post by:
C FAQ Q 13.2 says that sprintf() is guaranteed to work only for n <= 509: sprintf(dest, "%.*s", n, source); Does 509 appear in any C standard? If not, where it came from? I looked over C99...
2
by: Maxim Kazitov | last post by:
Hi, Is it possible to override MainWndProc using c# (for window in other process). Thanks, Max
1
by: Paul E Collins | last post by:
I am writing a class that inherits from TextBox. My class, UndoTextBox, extends the standard control by enabling multi-level undo and redo operations. Annoyingly, some of the affected properties...
2
by: Joseph Geretz | last post by:
I'm developing a Web Service using DIME to download and upload files from and to an IIS server. In order to increase the download filesize to unlimited, I have the following block in my App.config...
21
by: bonk | last post by:
Did anyone EVER come across the need to use "new" in a method declaration (breaking with inheritance / versioning)? Allthough I know what it does and how it is used I really have a hard time to...
3
by: SDRoy | last post by:
Hi Is there a limitation on how many rows can be fetched using a SqlDataReader in ASP.Net 2.0 ? -- Thanks, SDRoy
4
by: Ole Nielsby | last post by:
Here comes a generic class with a static property. Let's say they are exotic singleton pets. abstract class Pet {...} abstract class SharedPet<T>: Pet where T: SharedPet<T>, new() {...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.