Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

static constructors in C#

Anyone know if I'm right in thinking that an inherited static ctor won't always be called in C#.

Here's an example:

class parent
{
    static string foo;
    static parent()
    {
        foo = "parent static ctor called";
    }
}

class child : parent
{
    static child()
    {
        foo = "child static ctor called";
    }

    static public void staticMethod()
    {
        //sometimes this will output "Parent static ctor called"
        Console.WriteLine( foo };
    }
}

Now maybe I'm missing something here, but surely if I call a class's static method, /it's/ static ctor should have been called before hand, not it's /supertypes/ ctor.

What obvious thing am I missing here folks? ;)

TIA,

Pete

Pete Hodgson
Saturday, March 08, 2003

Hmm, looking back at that post it doesn't make any sense....

Here's a better example - lets say I want a supertype to define a method that prints out the name of the class. So I create the following:

    class Parent
    {
        static protected string className;
        static Parent()
        {
            className = "Parent";
        }
        static public void OutputClassName()
        {
            System.Console.Out.WriteLine( className );
        }
    }

    class ChildA : Parent
    {
        static ChildA()
        {
            className = "ChildA";
        }
    }

    class ChildB : Parent
    {
        static ChildB()
        {
            className = "ChildB";
        }
    }

And I run the following code:

    Parent.OutputClassName();
    ChildA.OutputClassName();
    ChildB.OutputClassName();

Now I'd expect the output to be

Parent
ChildA
ChildB

but it's actually

Parent
Parent
Parent

So what am I missing here?

TIA,

Pete

Pete Hodgson
Saturday, March 08, 2003

The problem is that there is a single storage item, not three, and it's being initialized three times. The value that'll be there will be dependent on the order that the compiler chooses to call the three static constructors (i.e., random, for all intents and purposes).

Unless I've missed something, it's working as designed.

Brad (dotnetguy.techieswithcats.com)
Sunday, March 09, 2003

Brad is correct that the misunderstanding is that there is only one static field here.  However, Brad's comment about the random order of the static constructors is not quite right.  The order in which static constructors are called is deterministic and documented in section 17.11 of ECMA specification 334.  To briefly sum up:

A static constructor is executed either when an instance of the class is created or when any static member of the class is referenced, whichever comes first.  Static constructors in the "Main" class of an exe are executed BEFORE Main is called.  Static constructors in classes with static member initializers are called IMMEDIATELY AFTER the static initializers run.

Eric Lippert
Tuesday, March 11, 2003

Actually, upon further reflection, Brad's not right about the "one slot" thing either.  He is right that there is one slot, but that alone is not the reason you are getting this behaviour.  The real reason is that the ChildA and ChildB static constructors have never been called!

Look at the rules I just posted -- no static members of ChildA or ChildB have been called, and no instances have been created, and neither has a "Main" method.  Nothing has caused the static initializer to run!

If you changed your program to

Parent.Output();
a = new ChildA();
ChildA.Output();
b = new ChildB();
ChildB.Output();

then you'd get all the static constructors running and changing that one slot.

Eric

Eric Lippert
Tuesday, March 11, 2003

I picked the words "random, for all intents and purposes" because I didn't really feel like explaining an effect that was tangential to the problem at hand (that there is only one data slot, not three as is supposed by the original poster).

Brad (dotnetguy.techieswithcats.com)
Tuesday, March 11, 2003

Thanks for the replies.

I think my misunderstanding of this is bacause I thought that the static members of a class would 'have' inheritance in a similar way to instance members.

I assumed that if I called a base class's static method in the context of the derived class, then the method would use the /derived/ class's static members in the method, not the base classes. That would analogous to the inheritance polymorphism displayed in non-static cases, no?

Is there a reason why the static aspects of a class don't exhibit this polymorphism? I'd imagine it'd be slightly easier to implement as (I think) that the binding could be done at compile-time.

Pete Hodgson
Wednesday, March 12, 2003

Actually, this isn't so much a static thing as a data thing. When you declare data storage in a class, derived classes don't get a unique version of that data storage element. So if I write:

class ClassA
{
protected int SomeIntValue;
public ClassA() { SomeIntValue = 12; }
public int GetTheValue() { return SomeIntValue; }
}

class ClassB : ClassA
{
public ClassB() { SomeIntValue = 15;
public int GetTheValueHere() { return SomeIntValue; }
}

It doesn't matter whether you call GetTheValue() or GetTheValueHere(), you'll always get 15. There is a single data slot for storage.

When you extend this into static, it still holds true. There is a single data element, except it's per-class, not per-object, and derived classes get the SAME data element, not a new one.

For virtual dispatching of data value, I use virtual properties. Static just isn't going to cover you here. There is no virtual support for statics (presumably because of performance).

Good luck!

Brad (dotnetguy.techieswithcats.com)
Wednesday, March 12, 2003

When you say "there's no virtual support for statics" that was what I was getting at. I'm not the best at explaining my self on paper :)

I agree with you on the virtual properties thing, it just seems a bit of a waste of computation to do all that method calling when a virtual static variable could be resolved at run time. Ah well, I guess we'll just have to wait until C# gets generics so we can do it properly.

Cheers,

Pete

Pete Hodgson
Wednesday, March 12, 2003

sorry, I meant to say a virtual static variable could be resolved at COMPILE-time

Pete Hodgson
Wednesday, March 12, 2003

Brad said

"There is no virtual support for statics (presumably because of performance)."

The statement is correct but the presumption doesn't really hold water.  There is no support for "virtual statics" because "virtual" and "static" are logical opposites, not because they'd be slow.

A "virtual method" in languages like C#, C++, etc, is  a single-dispatch method, where the dispatch is predicated on the run-time type of the _instance_.

But static methods by definition _never_ take an instance, so a "static virtual" is an oxymoron.

Even if it wasn't, there is not really a performance problem associated with virtual functions -- a vtable call adds a single pointer indirection to the jitted code. A pointer indirection will take a few machine cycles, tops -- a few billionths of a second.  There are plenty of bigger fish to fry.

Eric

Eric Lippert
Wednesday, March 12, 2003

I agree that a virtual static function is oxymoronic. However, I don't understand why C# doesn't support polymorphic static variables.

What I mean is: Say class Parent has a static method Parent::StaticMethod() which uses the static variable m_staticVar. Now class Child (deriving from Parent) redefines m_staticVar to a different value. Why couldn't the context in which StaticMethod() is called - Child.StaticMethod() or Parent.StaticMethod() - affect the way StaticMethod() works, because m_staticVar would be different in the two cases.  This polymorphism could be resolved at compile-time so would produce no overhead that I can see.

I guess this is kind of like another form of genericity (a.k.a. C++ templates).

Am I missing some obvious reason why this isn't feasible?

Pete Hodgson
Thursday, March 13, 2003

*  Recent Topics

*  Fog Creek Home