locked
base.base? RRS feed

  • 質問

  •  

    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

回答

  • You could do that in C# 1 (.NET 1.1), but not in C# 2 (.NET 2.0).  As far as I know you'd have to provide a method on B to provide a means of accessing A in that way.

     

    2007年8月22日 23:28
    モデレータ

すべての返信

  • You could do that in C# 1 (.NET 1.1), but not in C# 2 (.NET 2.0).  As far as I know you'd have to provide a method on B to provide a means of accessing A in that way.

     

    2007年8月22日 23:28
    モデレータ
  • Oops, sorry, I lied.  You could never do that.

    2007年8月22日 23:29
    モデレータ
  • 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
  • If the relationships among classes along an inheritance path are that complicated it suggests that your code probably needs some cleanup.

    2007年8月23日 6:06
  • ?  This is in no way a "complicated" scenario, and to reorganize would actually add complexity..

    2007年8月23日 12:58
  • In this specific case, wouldn't it be easier to put the implementation of B in A, minus the override, and have C and B both derive from A?

    2007年8月23日 16:15
  • Well, my suggestion would be:

    Code Snippet

    class A
    {
        protected void AFoo() {}
        public virtual void foo()   {AFoo();}
    }

    class B : A
    {
        public virtual void foo()   {....}
    }


    class C : B
    {
         public override void foo() { AFoo(); }
    }


    2007年8月23日 19:25
  • 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 could circumvent the OnMouseDown method and get at the third party library's private implementation details (which it's derived interfaces are), then you'd break their code.

    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/blog
    2008年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:51
  • ScottSt said:

    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.

    Some clarification is in order:
    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  
    In this example you will get an error at line 25.  Third does not have access to First.Protected so error CS1450 occurs.

    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();  
        }  
    }  
     
     
    In Third.Method, no amount of casting of this changes the fact that calls to PublicMethod will always be to Third.PublicMethod (and since, in this case, Third does not override Second.PublicMethod, Second.PublicMethod will always be the method called).

    If you prefer the syntax and semantics of C++ then yes, you'll have to write the code in C++.
    http://www.peterRitchie.com/blog
    2008年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(); 
        }  
    }  
     
    So don't tell me that I only have access to the protected members of my immediate base (parent) class, because I do have access to everything that is not private in all my ancestors unless it is virtual.  And that is what all of us who argue this problem consider the "bug".  The virtual keyword should add polymorphic behavior, but it should not remove the ability to call inherited functionality.  Even the new keyword used to replace inherited functionality doesn't remove the ability to call it, it just hides it so that it requires a cast to get to it.

    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:43
  • ScottSt said:

     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(); 
        }  
    }  
     
    So don't tell me that I only have access to the protected members of my immediate base (parent) class, because I do have access to everything that is not private in all my ancestors unless it is virtual.  And that is what all of us who argue this problem consider the "bug".  The virtual keyword should add polymorphic behavior, but it should not remove the ability to call inherited functionality.  Even the new keyword used to replace inherited functionality doesn't remove the ability to call it, it just hides it so that it requires a cast to get to it.

    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).

    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.

    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/blog
    2008年6月10日 13:49
    モデレータ
  • I get that same error, Peter.

    2008年6月10日 13:55
  • 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:26
  • I 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/blog
    2008年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:16
  • Being 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:53
  • 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.

    Completely agreed! I had exactly the same need recently when I had to extend a WinForms control that has buggy implementation. I had to override it and but I don't want to rewrite the code in the grandparent class which is working perfectly fine. I can do this in C++ very easily. Finally I found a workaround for this problem and create a library to solve this problem plus others. With this libaray, the original A/B/C problem can be easily solved as below:
        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