トップ回答者
base.base?

質問
-
I have a virtual function chain that spans three levels, and need to call a base class's base class's function.
This blog says "yeah, there's a way," but I'm not getting it to work:
http://dotnetnerd.blogspot.com/2002_02_01_archive.html
class
A { public virtual void foo() {} }class
B : A { public override void foo() {} }class
C : B { public override void foo() { ((A)base).foo(); } }The error:
error CS0175: Use of keyword 'base' is not valid in this context
2007年8月22日 22:43
回答
すべての返信
-
Yep. In my case, I worked around it with a protected flag to hint B.foo() to skip the B-specific stuff, and just delegate to A.foo().
Too bad that ((A)base) and/or base.base are not allowed...
Followup to your followup: so i guess that blogger lied too... The only thing worse than a liar is a C# liar...
2007年8月22日 23:32 -
There are tons of workarounds, and all of them mean that you don't use the ability to delegate to a grandparentclass in the same way you can delegate to parent base class. Accessibility of virtual functions should be no different than accessibility of non-virtual functions. Anyway, this is something that would be filed as a bug, but I'm going to leave it as a discussion here because it's not that important to me that it gets fixed... just an academic curiousity at this stage. I'm still curious what the theoretical reason, if any, the C# designers opted not to support base.base or ((A)base), and in general, why the base keyword doesn't act like a regular object like the this pointer does.
2007年8月23日 21:47 -
It works that way because a derived class is extending a public/protected interface of it's base; it needs to be able to delegate back to it's base (which is while the "base" keyword exists). The base of a class's base is an implementation detail. The ability to use "base.base" would allow a class the reach into the private implementation details of another class. In your example, the public/protected interface of B--while based on the public/protected interface of A--is not directly dependant on A. B can publish that public/protected interface with or without A. Should a third class (C in your example) reach in and get at the implementation details of B it would be tightly coupled. In your example, directly coupled to A. B would not be able to change it's implementation details because any other class could be dependant on them.
Your workaround to publish the A class in the B interface is an explicit choice to make that implementation detail public.
In C++, I don't think you can get at A's virtual method because both B an C have overridden it--even when you cast to A you should still be getting the instantiated object's method, not A's.
2007年8月24日 0:40モデレータ -
Actually in C++ it is very easy to call your grandparent's implementation of a virtual method. You just prefix it with that class name. In this case use "A.foo();".
And I run into this "bug" all the time and it frustrates be a lot. The most common time I run into it is when I am trying to modify the behavior of a control that is in a third party library which I can not modify.
For example I ran into it again today when trying to change the behavior of a Mouse Down event, which is exposed in a virtual OnMouseDown method, on a third party grid control. That grid control is based on it's own custom ScrollableControl which in turn is based on the framework Panel where the method originates. I wanted to change just the grid's drawing behavior but preserve the grandparent's ScrollableControl behavior.
What I wanted to do was:
protected override OnMouseDown( MouseEventArgs e )
{
base.base.OnMouseDown(e);
// My code to NOT focus the grid cell, but to draw it highlighted
}
Problem is you CAN'T do that for this very "bug" in C#.
So I either need to totally recreate all the functionality of OnMouseDown from Panel and ScrollableControl, or I have to call base.OnMouseDown just get get the grandparent class called, and then write a bunch of code to undo everything the parent class does.
Either way it make the code complexity go WAY WAY up.2008年6月5日 18:53 -
What if in the third party library OnMouseDown did this:
private bool mouseDown; protected override OnMouseDown(MouseEventArgs e) { mouseDown = true; base.OnMouseDown(e); } bool dragging; protected override OnMouseMove(MouseEventArgs e) { if(mouseDown && !dragging) { StartDragging(); } } protected override OnMouseUp(MouseEventArgs e) { mouseDown = false; base.OnMouseUp(e); }
If you're trying to do this either the 3rd party library is broken (and you should contact them for a fix), or you're not using OnMouseDown for it's intended purpose.
http://www.peterRitchie.com/blog2008年6月9日 21:16モデレータIn your example, if I were to override OnMouseDown and NOT call base.OnMouseDown I will "break their code", dragging in this case, no matter whether I can call base.base.OnMouseDown or not, so it is irrelevant what they are doing in their implementation.
In fact that is exactly the point. If I need to override a virtual method and have deemed it necessary to NOT call base.* then I have chosen to disable their functionality. If they don't want me to be able to do that, then their implementation should be private instead of protected.
Making a call to base.base.OnMouseDown does not exposed "private implementation" because if it were private I couldn't override it. The methods we are talking about are protected, which is code that is supposed to be available to descendants!
I don't consider a control broken just because the designer didn't forsee every possible use a consumer might make of it. In my case I'm use a grid control that does nearly everything I might want, but in one particular case in a window I'm working on, I want to create a "special" cell in the grid that reacts to the mouse down in it's own special way. But when I did that when calling the base class OnMouseDown it had undesired drawing behavior of the surrounding cells. When not calling the base class OnMouseDown the drawing was perfect but grid scrolling doesn't automatically occur when the cell is partally off screen because the base.base.OnMouseDown doesn't get called.
In the mean time my only solution (which I haven't done yet) is to create a managed C++ assembly just to write something like:
class SpecialCellGrid : Grid
{
private:
// Overide base class's OnMouseDown & hide it from our children
void OnMouseDown( MouseEventArgs e )
{
ScrollablePanel::OnMouseDown( e );
SpecialOnMouseDown( e );
}
protected:
// Expose a new "special" OnMouseDown to our children
void virtual SpecialOnMouseDown( MouseEventArgs e )
{}
}
And then in my window use that grid and override SpecialOnMouseDown.
It seems like a damn shame to have to create and ship an entirely separate assembly for so fe lines of code just because C# can't do something so basic as call specific ancester functionality.- 編集済み ScottSt 2008年6月9日 21:52 fix typos
2008年6月9日 21:51ScottSt said:Some clarification is in order:In your example, if I were to override OnMouseDown and NOT call base.OnMouseDown I will "break their code", dragging in this case, no matter whether I can call base.base.OnMouseDown or not, so it is irrelevant what they are doing in their implementation.
In fact that is exactly the point. If I need to override a virtual method and have deemed it necessary to NOT call base.* then I have chosen to disable their functionality. If they don't want me to be able to do that, then their implementation should be private instead of protected.
Making a call to base.base.OnMouseDown does not exposed "private implementation" because if it were private I couldn't override it. The methods we are talking about are protected, which is code that is supposed to be available to descendants!
I don't consider a control broken just because the designer didn't forsee every possible use a consumer might make of it. In my case I'm use a grid control that does nearly everything I might want, but in one particular case in a window I'm working on, I want to create a "special" cell in the grid that reacts to the mouse down in it's own special way. But when I did that when calling the base class OnMouseDown it had undesired drawing behavior of the surrounding cells. When not calling the base class OnMouseDown the drawing was perfect but grid scrolling doesn't automatically occur when the cell is partally off screen because the base.base.OnMouseDown doesn't get called.
In the mean time my only solution (which I haven't done yet) is to create a managed C++ assembly just to write something like:
class SpecialCellGrid : Grid
{
private:
// Overide base class's OnMouseDown & hide it from our children
void OnMouseDown( MouseEventArgs e )
{
ScrollablePanel::OnMouseDown( e );
SpecialOnMouseDown( e );
}
protected:
// Expose a new "special" OnMouseDown to our children
void virtual SpecialOnMouseDown( MouseEventArgs e )
{}
}
And then in my window use that grid and override SpecialOnMouseDown.
It seems like a damn shame to have to create and ship an entirely separate assembly for so fe lines of code just because C# can't do something so basic as call specific ancester functionality.
Access to protected members of a class is only available to the derived class (and the base class), not by all classes further down the hierarchy. For example,
1 using System; 2 3 public class Program { 4 public static void Main() { 5 Third p = new Third(); 6 p.Method(); 7 } 8 } 9 10 public class First { 11 protected virtual void Protected(){ 12 Console.WriteLine("First"); 13 } 14 } 15 16 public class Second : First { 17 protected override void Protected(){ 18 Console.WriteLine("Second"); 19 } 20 } 21 22 public class Third: Second { 23 public void Method() { 24 First p = this; 25 p.Protected(); 26 } 27 } 28
So, even if "base" was accessible from base, in you're example, you'd still not have access to OnMouseDown via base.base.OnMouseDown(e).
A class simply does not have access to any protected members of any other class except the protected members of it's base class. The protected members of a base's base are private implementation details.
If OnMouseDown was public and not virtual, you could simply cast the this object to another type. If OnMouseDown was public and virtual, you would break polymorphism by gaining access to a base's base virtuals. i.e. the runtime type defines what method is called, no amount of casting can change that. For example:
using System; public class Program { public static void Main() { Third p = new Third(); p.Method(); } } public class First { public virtual void PublicMethod(){ Console.WriteLine("First"); } } public class Second : First { public override void PublicMethod(){ Console.WriteLine("Second"); } } public class Third: Second { public void Method() { First p = this; PublicMethod(); p.PublicMethod(); } }
If you prefer the syntax and semantics of C++ then yes, you'll have to write the code in C++.
http://www.peterRitchie.com/blog2008年6月9日 23:52モデレータYoud say "Access to protected members of a class is only available to the derived class (and the base class), not by all classes further down the hierarchy"
Yet if I drop the keywords virtual and override then rename the methods in your example, then it will run just fine.
using System; public class Program { public static void Main() { Third p = new Third(); p.Method(); } } public class First { protected void FirstPublicMethod(){ Console.WriteLine("First"); } } public class Second : First { protected void SecondPublicMethod(){ Console.WriteLine("Second"); } } public class Third: Second { public void Method() { First p = this; SecondPublicMethod(); p.FirstPublicMethod();
FirstPublicMethod();} }
I realize that using a cast wouldn't get you to base class polymorphic code, because it is polymorphic. That is why another syntax is needed. I personally would prefer something more akin to C++ where you use the class name instead of base, because using "base.base.Protected()" is ambiguous, where using "First.Protected()" is not.
I consider C# and C++ syntax very similar and am quite comfortable using either. There are pros and cons to both, but all in all C# is generally a better choice for GUI applications considering the weak skill sets I've been seeing in college grads the last 10 years. My only real gripes with C# are this "bug" we are discussing, and Dispose() semantics (but that is another discussion).- 編集済み ScottSt 2008年6月10日 12:46 make methods protected
2008年6月10日 12:43ScottSt said:I get "file.cs(26,11): error CS1540: Cannot access protected member 'First.FirstPublicMethod()' via a qualifier of type 'First'; the qualifier must be of type 'Third' (or derived from it)" in the code you posted.Youd say "Access to protected members of a class is only available to the derived class (and the base class), not by all classes further down the hierarchy"
Yet if I drop the keywords virtual and override then rename the methods in your example, then it will run just fine.
using System; public class Program { public static void Main() { Third p = new Third(); p.Method(); } } public class First { protected void FirstPublicMethod(){ Console.WriteLine("First"); } } public class Second : First { protected void SecondPublicMethod(){ Console.WriteLine("Second"); } } public class Third: Second { public void Method() { First p = this; SecondPublicMethod(); p.FirstPublicMethod();
FirstPublicMethod();} }
I realize that using a cast wouldn't get you to base class polymorphic code, because it is polymorphic. That is why another syntax is needed. I personally would prefer something more akin to C++ where you use the class name instead of base, because using "base.base.Protected()" is ambiguous, where using "First.Protected()" is not.
I consider C# and C++ syntax very similar and am quite comfortable using either. There are pros and cons to both, but all in all C# is generally a better choice for GUI applications considering the weak skill sets I've been seeing in college grads the last 10 years. My only real gripes with C# are this "bug" we are discussing, and Dispose() semantics (but that is another discussion).
1 using System; 2 3 public class Program { 4 public static void Main() { 5 Third p = new Third(); 6 p.Method(); 7 } 8 } 9 10 public class First { 11 protected void FirstPublicMethod(){ 12 Console.WriteLine("First"); 13 } 14 } 15 16 public class Second : First { 17 protected void SecondPublicMethod(){ 18 Console.WriteLine("Second"); 19 } 20 } 21 22 public class Third: Second { 23 public void Method() { 24 First p = this; 25 SecondPublicMethod(); 26 p.FirstPublicMethod(); 27 FirstPublicMethod(); 28 } 29 } 30
http://www.peterRitchie.com/blog2008年6月10日 13:49モデレータThat's what I get for copying and pasting your code and trying to edit it here.
Remove the 2 unnecessary "polymorphic" lines
First p = this;
p.FirstPublicMethod();
then it runs just fine and calls directly to a grand-parent method as I was trying to show.
But I'm sure you know full well that works, otherwise none of us would get very far developing class heirarchies without making nearly everything public.
I'll agree that if I were writing 3+ levels of classes a program I would make architecural changes so that I wouldn't need to make calls directly to a grandparent virtual. But when it comes to GUI development, you can't buy everything, so you either start from scratch or build on an existing control, in which case you can't change the ancestor code. But whatever is "published" as either public or protected should be able to be leveraged.
If the ability were available then this discussion would probably be about whether anyone should use it or not and everyone could agree to disagree, but we could all get our work done. It is too bad Mike Stall's hack for doing inline IL isn't safe for production code or I'd use that to make the desired call.2008年6月10日 17:26I get that you've got a corner case where bad design is causing you grief. You would not want a design that couples a class to a parent AND A grandparent so things like "base.base" or access to a grandparent's protected without going through the current object (this) aren't part of the language.
Better design should be the solution to this, not adding syntax to the language.
http://www.peterRitchie.com/blog2008年6月10日 19:20モデレータHello,
I don't want a philosphical discussion about better design and other blablas.
It is absolutly valid if someone wants to access a method two hierachies above or more.It's absolutly simple in C++. I loved C++ for over 15 years. When I saw C#, it was clear to get rid of C++.
But accesing a grandparent member does not to be discussed away with: Uh, ah, you need a better architecture, because it is always subjective and NOT measurable what a better architecture is.
So I aggree that it is necessary to have the in a language.We had the problem too.
A thir party product overrides a virtual method of a base class, but the code was buggy.
Until waiting for a fix (months!!!), it would be absolutly right to call the original implementation and to work with a little bit featureless but correct implementation than a wrong implementation.
2009年1月27日 12:16Being able to call a method on any base in the inheritance hierarchy would be great. In most cases, when I inherit from someonelse's class I do know all about it's base class, especially when I'm working with web controls. I have lots of code that handles their class as a generic Control so if they changed their "private" implementation to not inherit from Control, my code would break. So, just like accessing a class through its base class is not a "violation" of "object-orientedness", I don't think calling a method on its base class would be a violation.
I run into this quite often with web controls, where I want to change some little behavior of a control. I just create my own class based on the control I want, then override the methods I want. The problem comes in when I still want the original base class's implementation to execute, but not my direct base class's method. There really seems to be no way to do this in C#, but it would be a very nice and practical thing to have.
My specific issue right now is a bug in a SharePoint control's CreateChildControls method. The easiest fix would be to implement my own CreateChildControls method, call the base's base CreateChildControls and implement the rest myself. Instead, I have to let it execut the base class's method and then write a bunch of ugly code to find and correct the bug. And reporting the bug, waiting for it to be officially recognized, fixed and distributed by the SharePoint team is not an option (the customer needs the functionality today).
So, my vote is for a feature request (since I guess it isn't strictly considered a bug) to add the ability to call any base code you want as part of C#.2009年3月11日 17:53A thir party product overrides a virtual method of a base class, but the code was buggy.
Until waiting for a fix (months!!!), it would be absolutly right to call the original implementation and to work with a little bit featureless but correct implementation than a wrong implementation.
class C : B { private static readonly Action<A> baseBaseFoo = typeof(A).GetNonVirtualInvoker<Action<A>>("foo"); public override void foo() { baseBaseFoo(this); } }
For detail of how, see C#.Net Calling Grandparent's Virtual Method (base.base in C#)2009年5月23日 14:22