Skip to main content

Virtual member call in a constructor


I'm getting a warning from ReSharper about a call to a virtual member from my objects constructor. Why would this be something not to do?



Source: Tips4allCCNA FINAL EXAM

Comments

  1. (Assuming you're writing in C# here)

    When an object written in C# is constructed, what happens is that the initializers run in order from the most derived class to the base class, and then constructors run in order from the base class to the most derived class (see Eric Lippert's blog for details as to why this is).

    Also in .NET objects do not change type as they are constructed, but start out as the most derived type, with the method table being for the most derived type. This means that virtual method calls always run on the most derived type.

    When you combine these two facts you are left with the problem that if you make a virtual method call in a constructor, and it is not the most derived type in its inheritance hierarchy, that it will be called on a class whose constructor has not been run, and therefore may not be in a suitable state to have that method called.

    This problem is, of course, mitigated if you mark your class as sealed to ensure that it is the most derived type in the inheritance hierarchy - in which case it is perfectly safe to call the virtual method.

    ReplyDelete
  2. The rules of C# are very different from that of Java and C++.

    When you are in the constructor for some object in C#, that object exists in a fully initialized (just not "constructed") form, as its fully derived type.

    namespace Demo
    {
    class A
    {
    public A()
    {
    System.Console.WriteLine("This is a {0},", this.GetType());
    }
    }

    class B : A
    {
    }

    // . . .

    B b = new B(); // Output: "This is a Demo.B"
    }


    This means that if you call a virtual function from the constructor of A, it will resolve to any override in B, if one is provided.

    Even if you intentionally set up A and B like this, fully understanding the behavior of the system, you could be in for a shock later. Say you called virtual functions in B's constructor, "knowing" they would be handled by B or A as appropriate. Then time passes, and someone else decides they need to define C, and override some of the virtual functions there. All of a sudden B's constructor ends up calling code in C, which could lead to quite surprising behavior.

    It is probably a good idea to avoid virtual functions in constructors anyway, since the rules are so different between C#, C++, and Java. Your programmers may not know what to expect!

    ReplyDelete
  3. In order to answer your question, consider this question: what will the below code print out when the Child object is instantiated?

    class Parent
    {
    public Parent()
    {
    DoSomething();
    }
    protected virtual void DoSomething() {};
    }

    class Child : Parent
    {
    private string foo;
    public Child() { foo = "HELLO"; }
    protected override void DoSomething()
    {
    Console.WriteLine(foo.ToLower());
    }
    }


    The answer is that in fact a NullReferenceException will be thrown, because foo is null. An object's base constructor is called before its own constructor. By having a virtual call in an object's constructor you are introducing the possibility that inheriting objects will execute code before they have been fully initialized.

    ReplyDelete
  4. Reasons of the warning are already described, but how would you fix the warning? You have to seal either class or virtual member.

    class B
    {
    protected virtual void Foo() { }
    }

    class A : B
    {
    public A()
    {
    Foo(); // warning here
    }
    }


    You can seal class A:

    sealed class A : B
    {
    public A()
    {
    Foo(); // no warning
    }
    }


    Or you can seal method Foo:

    class A : B
    {
    public A()
    {
    Foo(); // no warning
    }

    protected sealed override void Foo()
    {
    base.Foo();
    }
    }

    ReplyDelete
  5. In C#, a base class' constructor runs before the derived class' constructor, so any instance fields that a derived class might use in the possibly-overridden virtual member are not initialized yet.

    Do note that this is just a warning to make you pay attention and make sure it's all-right. There are actual use-cases for this scenario, you just have to document the behavior of the virtual member that it can not use any instance fields declared in a derived class below where the constructor calling it is.

    ReplyDelete
  6. Yes, it's generally bad to call virtual method in the constructor.

    At this point, the objet may not be fully constructed yet, and the invariants expected by methods may not hold yet.

    ReplyDelete
  7. Because until the constructor has completed executing, the object is not fully instantiated. Any members referenced by the virtual function may not be initialised. In C++, when you are in a constructor, this only refers to the static type of the constructor you are in, and not the actual dynamic type of the object that is being created. This means that the virtual function call might not even go where you expect it to.

    ReplyDelete
  8. Your constructor may (later, in an extension of your software) be called from the constructor of a subclass that overrides the virtual method. Now not the subclass's implementation of the function, but the implementation of the base class will be called. So it doesn't really make sense to call a virtual function here.

    However, if your design satisfies the Liskov Substitution principle, no harm will be done. Probably that's why it's tolerated - a warning, not an error.

    ReplyDelete
  9. There's a difference between C++ and C# in this specific case.
    In C++ the object is not initialized and therefore it is unsafe to call a virutal function inside a constructor.
    In C# when a class object is created all its members are zero initialized. It is possible to call a virtual function in the constructor but if you'll might access members that are still zero. If you don't need to access members it is quite safe to call a virtual function in C#.

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex