none
How to call a virtual method of an ancestor class that is higher than the base class RRS feed

  • Question

  • I'm trying to create a derived class from UpdatePanel (which in turn is derived from System.Web.UI.Control) that will allow the tag to be specified as something other than <div> or <span>.  I'm overriding the virtual RenderChildren method and would like to call Control.RenderChildren from my override.  I can't do it with base.RenderChildren because that refers to UpdatePanel.RenderChildren.  base.base.RenderChildren(...) doesn't compile, neither does ((Control)base).RenderChildren(...) or ((Control)this).RenderChildren(...).

    Using reflection doesn't work either.  typeof(Control).GetMethod("RenderChildren") followed by MethodInfo.Invoke passing my derived class instance as the first parameter results in my RenderChildren method being called, not Control.RenderChildren.  There must be some BindingFlag or something that will do the equivalent of an MSIL 'call' rather than 'callvirt'

    Is there any simple way to do this?  Or is the only way to re-implement UpdatePanel from scratch?


    tushka

    Tuesday, February 7, 2012 10:00 PM

All replies

  • I think it is not necessary to use reflection to do that.

    Actually, the base.method has been override. So you can not call base.method directly. I think you can do it as below:

    class A
        {
            public A()
            { Console.WriteLine("A");
            }
    
            public virtual void method()
            {
                Console.WriteLine("method in A");
            }
        }
        class B : A
        {
            public override void method()
            {
                Console.WriteLine("method in B");
            }
            public void method2()
            {
                base.method();
                Console.WriteLine("call completed");
            }
        }
        class C : B
        {
            public override void method()
            {
                Console.WriteLine("method in C");
            }
    
            public void method2()
            {
                base.method2();
            }
        }

    We can define another method to call the base.method you need to call.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, February 8, 2012 9:52 AM
  • Paul, thanks for the response.  This will work in the case where you can modify class B to define another method, but in my case class B (System.Web.UI.UpdatePanel) (and A, System.Web.UI.Control) are both framework classes, and so cannot be modified.

    If I'm not mistaken, this could be done if the override method is written in MSIL, by acquiring a method handle to Control.RenderChildren and calling it via an MSIL 'call' rather than 'callvirt'.  That being the case, there should be some way to do the same thing in C#, if not directly then at least by means of reflection


    tushka

    Wednesday, February 8, 2012 3:53 PM
  • I am afraid it is difficult to achieve your requirement.

    Using reflection is the same way as call:

    class C : B
        {
            public override void method()
            {
                Console.WriteLine("method in C");
            }
    
            public void method2()
            {
    
                A a = this;
                a.method();   
            }

    If you want to call method in Parent class's Parent class(Base.Base), I am afraid that you can not use inherited class instance to call the method, because the method has been override and implement in inherited class. So if you use inherit class instance to create a parent class instance, the override method would be called. The only way to call it is that create a Control instance use constructor of itself.

    Moreover, actually, you can implement the method in your Class C. Let's have a look at source code of UpdatePannel class:

    protected internal override void RenderChildren(HtmlTextWriter writer)
    {
        if (this._asyncPostBackMode)
        {
            if (this._rendered)
            {
                return;
            }
            HtmlTextWriter writer2 = new HtmlTextWriter(new StringWriter(CultureInfo.CurrentCulture));
            base.RenderChildren(writer2);
            PageRequestManager.EncodeString(writer, "updatePanel", this.ClientID, writer2.InnerWriter.ToString());
        }
        else
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
            if (this._attributes != null)
            {
                this._attributes.AddAttributes(writer);
            }
            if (this.RenderMode == UpdatePanelRenderMode.Block)
            {
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
            }
            else
            {
                writer.RenderBeginTag(HtmlTextWriterTag.Span);
            }
            base.RenderChildren(writer);
            writer.RenderEndTag();
        }
        this._rendered = true;
    }
    

    I use Reflector to see the code. You can see that Base.RenderChildren method would be called. So you can implent RenderChildren method in your user control class to call base.RenderChildren. It would call the method in Control.RenderChildren.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    Friday, February 10, 2012 6:55 AM
  • Not sure I'm following your line of reasoning, I think what you are saying is that if I copy the implementation of RenderChildren from UpdatePanel and paste it into my class (class C : UpdatePanel) under a different name (perhaps RenderChildren2 or something?), then my class C::RenderChildren override can call this.RenderChildren2 instead of base.RenderChildren, and the line of code in RenderChildren2 that calls base.RenderChildren will then call Control.RenderChildren, rather than UpdatePanel.RenderChildren?

    Assuming that my understanding of what you are saying is correct, there's a couple of problems here, first of all it won't compile because this._asyncPostBackMode and this._rendered are private to UpdatePanel, second, as long as my class C inherits UpdatePanel any reference to base.RenderChildren will still call UpdatePanel.RenderChildren.  In order for this to work, my class C would have to re-implement not just RenderChildren but the entire UpdatePanel, then can inherit Control instead of UpdatePanel

    Another thing I tried, calling thru a delegate, didn't work either:

    public class CustomUpdatePanel : UpdatePanel, IAttributeAccessor { private delegate void RenderChildrenDelegate(HtmlTextWriter writer); ... protected override void RenderChildren(HtmlTextWriter writer) { ... MethodInfo mi = typeof(Control).GetMethod("RenderChildren", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[]{typeof(HtmlTextWriter)}, null); RenderChildrenDelegate dg = (RenderChildrenDelegate)Delegate.CreateDelegate(typeof(RenderChildrenDelegate), (Control)this, mi); dg.Invoke(writer); ... } ... }

    Apparently, it's pretty well locked down.  Not sure if that's by design or an oversight,   But again, it seems to me that if the .Net framework (MSIL call vs callvirt) supports it, then
    there should be a way to do it in C#


    tushka

    Saturday, February 11, 2012 1:49 AM
  • Well, you misunderstand my point in last relpy.

    I mean that in the RenderChildren method of UpdatePanel class, base.RenderChildren(it is Control.RenderChildren) method would be called. So you can implement RenderChildren method in your CustomUpdatePanel class:

    public class CustomUpdatePanel : UpdatePanel, IAttributeAccessor
            {
    
                    protected override void RenderChildren(HtmlTextWriter writer)
                    {
                       base.RenderChildren(writer);    
                    }
            }

    It actually will call base.base.RnderChildren method.

    Morover, in my opinion, your purpose is not a good design. You are designing a custom control that inherited form UpdatePanel, why you still need to implemend method in Control class?

    And as you said, MSIL can call Control.RenderChildren method. But it needs a instance of Control Class. You have a CustomUpdatePanel that inherited from UpdatePanel that inherited from Control class. Even you convert a instance of CUstomUpdatePanel to Control, it is actually still a instance of CustomUpdatePanel, and the RenderChildren method has been override, so the method in Control class can not be called.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    Monday, February 13, 2012 8:01 AM
  • UpdatePanel can only render as a <div> if UpdatePanel.RenderMode = Block, or a <span>if UpdatePanel.RenderMode = Inline.  My derived version can render as any element.  It has a TagName property that specifies what element to render.  It can also render any custom attributes including style attributes (IAttributeAccessor).

    My implementation is as follows.  If it's in an AJAX partial postback (_asyncPostBackMode/IsInPartialRendering == true) it defers to UpdatePanel.RenderChildren (here I DO want to call UpdatePanel.RenderChildren).  otherwise (if it's a full page rendering), UpdatePanel.RenderChildren will render either a <div> or <span> begin tag, then call Control.RenderChildren, and finally render the end tag.  I DON'T want to do this, rather I want to render the begin/end tag as specified by my TagName property (including custom attributes/styles), and in between call Control.RenderChildren (NOT UpdatePanel.RenderChildren):

                    protected override void RenderChildren(HtmlTextWriter writer)
                    {
                            IEnumerator en = null;
                            IDisposable d = null;
                            if (IsInPartialRendering || String.IsNullOrEmpty(TagName))
                            {       // use default implementation if partial rendering or no tag specified
                                    base.RenderChildren(writer);  // here I DO want to call UpdatePanel.RenderChildren
                                    return;
                            }
                            // not partial rendering, tag specified -
                            // set id attribute for tag
                            writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);
                            // write custom attributes if any
                            if (_attributes != null)
                            {
                                    foreach (string key in _attributes.Keys)
                                    {
                                            writer.AddAttribute(key, _attributes[key], true);
                                    }
                            }
                            // write opening tag for specified tag
                            writer.RenderBeginTag(TagName);
                            base.RenderChildren(writer);  //  I want to call Control.RenderChildren here NOT UpdatePanel.RenderChildren
    
                             writer.RenderEndTag();
                    }
    


    tushka

    Monday, February 13, 2012 11:56 PM
  • OK, I know what you mean. Let me explain that why your purpose is not able to achieved.

    Please see the declaration of Control.RenderChildren method in the source code of Control(You can use Reflector to check that):

    protected internal virtual void RenderChildren(HtmlTextWriter writer);

    The method is symbolized with "protected internal". This means The type or member can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly. Access from another assembly must take place within a class declaration that derives from the class in which the protected internal element is declared, and it must take place through an instance of the derived class type.

    So Control.RenderChildren can be called from UpdatePanel class which is inherited from Control class, but it is not able to called from CustomUpdatePanel because the class is inherited from UpdatePanel, not from Control. Even using reflection, it is the same.

    I hope you can understand it.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us


    • Edited by Paul Zhou Tuesday, February 14, 2012 6:54 AM
    Tuesday, February 14, 2012 6:54 AM
  • Paul are you saying I can't access any protected member of Control class  because I'm inheriting from UpdatePanel?  If my class inherits UpdatePanel then it also inherits Control and all the way up the chain to System.Object, and as such should have access to all protected members up the chain (designation of "protected internal" and "protected" should be equivalent here since it's protected OR internal not protected AND internal).  If UpdatePanel didn't override RenderChildren then any reference in CustomUpdatePanel to base.RenderChildren would call Control.RenderChildren.

    For instance the following call:

    base.CreateChildControls();

    will generate the following MSIL (note access to protected Control member and call rather than callvirt which will specifically call Control.CreateChildControls):

     IL_0005:  ldarg.0
      IL_0006:  call       instance void [System.Web]System.Web.UI.Control::CreateChildControls()

    whereas this call:

    CreateChildControls();

    will generate this (note callvirt, if CreateChildControls was overridden then the override would be called):

    IL_000c: ldarg.0

    IL_000d: callvirt instance void [System.Web]System.Web.UI.Control::CreateChildControls()

    similarly this:  

    base.RenderChildren(writer);


    will generate:

    IL_0031: ldarg.0

    IL_0032: ldarg.1

    IL_0033: call instance void [System.Web.Extensions]System.Web.UI.UpdatePanel::RenderChildren(class [System.Web]System.Web.UI.HtmlTextWriter)

    So it seems as though there should be a way write C# code (if not directly thru some new keyword such as base.base or something like that, then thru reflection or calling via a delegate) that will effectively generate this which will explicitly call Control.RenderChildren since it's a call not a callvirt:

    IL_0031: ldarg.0

    IL_0032: ldarg.1

    IL_0033: call instance void [System.Web]System.Web.UI.Control::RenderChildren(class [System.Web]System.Web.UI.HtmlTextWriter)

    I can't prove that this will work without actually re-implementing my class in MSIL, but it seems logical to me that it should work

    tushka





    • Edited by tushka Tuesday, February 14, 2012 1:53 PM add more stuff
    Tuesday, February 14, 2012 1:29 PM
  • I've re-implemented the CustomUpdatePanel class in Managed C++, seems to work!

    Would be nice if it could be made to work in C# as well!

    void CustomUpdatePanel::RenderChildren(HtmlTextWriter^ writer)
    {
            IEnumerator^ en = nullptr;
            IDisposable^ d = nullptr;
            if (IsInPartialRendering || String::IsNullOrEmpty(TagName))
            {       // use default implementation if partial rendering or no tag specified
                    UpdatePanel::RenderChildren(writer);
                    return;
            }
            // not partial rendering, tag specified -
            // set id attribute for tag
            writer->AddAttribute(HtmlTextWriterAttribute::Id, ClientID);
            // write custom attributes if any
            if (_attributes != nullptr)
            {
                    for each (String^ key in _attributes->Keys)
                    {
                            writer->AddAttribute(key, _attributes[key], true);
                    }
            }
            // write opening tag for specified tag
            writer->RenderBeginTag(TagName);
            // write children
            Control::RenderChildren(writer);
            // write closing tag for specified tag
            writer->RenderEndTag();
    }
    


    tushka

    Wednesday, February 15, 2012 12:22 AM
  • Well, if it can be implemented in managed C++, I think it is difference between C# and C++ compiler.

    I still consider that it is impossible in C#. The Control.CreateChildControl method can be called from CustomUpdatePanel class is because that the method is not overrided in UpdatePanel class. If you can use some tools or debug the assembly(WinDbg etc) to check method table(method token), you will find that base.CreateChildControl method exists in Control class, but base.RenderChildren exists in UpdatePanel class. It is different between override and non-override methods.

     

    Anyway, I am glad to hear that you can implement it in managed C++.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    • Proposed as answer by Paul Zhou Thursday, February 16, 2012 8:13 AM
    • Unproposed as answer by tushka Friday, February 17, 2012 4:47 AM
    Wednesday, February 15, 2012 7:18 AM
  • Yeah it's impossible in C#, but it shouldn't be, that's my point.  Since you work at MSFT maybe you could bring this up to the product team as something that should be remedied, C++ allows explicitly specifying which method to call (Control::RenderChildren(...)) or allowing the compiler to decide (__super::RenderChildren which works the same as base in C#).  Seems like a simple matter to implement some syntax such as casting the base keyword for explicit ((Control)base).RenderChildren(...) in addition to the existing base.RenderChildren(...) with no cast to let the compiler decide.

    So bottom line, I think it's an issue, but since no one else seems to care one way or the other, so I guess it will remain that way.

    Thanks anyway


    tushka

    Wednesday, February 15, 2012 2:42 PM