0

Here is a program that does not trigger some static constructors though it displays distinct owned static instances.

class Program
{
    static void Main(string[] args)
    {
        Template.Render();
        Template2.Render();
        Console.WriteLine(ReferenceEquals(Template.Factory, Template2.Factory));
    }
}

public class BaseTemplate<T> where T : new()
{
    static BaseTemplate()
    {
        Console.WriteLine("BaseTemplate() " + typeof(T).Name);
    }

    public static object Factory = new object();

    public static void Render()
    {
        Console.WriteLine("Render() from " + typeof(T).Name);
    }
}

public class Template : BaseTemplate<Template>
{
    static Template()
    {
        Console.WriteLine("Static ctor()");
    }
}

public class Template2 : BaseTemplate<Template2>
{
    static Template2()
    {
        Console.WriteLine("Static ctor() 2");
    }
}

The result

BaseTemplate() Template
Render() from Template
BaseTemplate() Template2
Render() from Template2
False

The goal here is to have a custom instance of Factory for each sub-class, which works fine, but also to initialize it in a static constructor. We can see the Factory instances are distinct from the reference test, but the static constructors are not called unless specifically called with System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);

Then the result is the following.

Static ctor()
BaseTemplate() Template
Render() from Template
Static ctor() 2
BaseTemplate() Template2
Render() from Template2
False
John Wu
  • 50,556
  • 8
  • 44
  • 80
Sébastien Ros - MSFT
  • 5,091
  • 29
  • 31
  • 1
    Static constructor called before you create instance of type or access static member. You do not do any of this, so static constructor is not called. – user4003407 Aug 14 '17 at 22:17
  • _"but also to initialize it in a static constructor"_ -- it _is_ initialized in a static constructor (the base type constructor), just not the one you apparently wanted. There's nothing in your question that would indicate what the point of the derived types are, but the static constructor execution rules are clear and well-documented. Note that if the `Factory` field were actually of type `T`, the `T` static constructor _would_ get called. It still wouldn't be clear _why_ you wanted that, but at least it would happen. – Peter Duniho Aug 14 '17 at 22:27

1 Answers1

2

From the documentation:

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only. It is called automatically before the first instance is created or any static members are referenced.

So to trigger the static ctor you must either

  1. Create an instance of Template or Template2. Your example doesn't.

  2. Call a static member of Template or Template2. In your example, neither class has any static members (other than the ctor) so you obviously aren't calling them.

If you modify your code:

Template.Render();
Template2.Render();
var o = new Template();
var p = new Template2();
Console.WriteLine(ReferenceEquals(Template.Factory, Template2.Factory));

The output is:

BaseTemplate() Template
Render() from Template
BaseTemplate() Template2
Render() from Template2
Static ctor()
Static ctor() 2
False

With static methods there is no inheritance (that requires a VMT and therefore an instance) but you could chain the static constructor calls like this:

public class BaseTemplate<T> where T : new()
{
    static BaseTemplate()
    {
        Console.WriteLine("BaseTemplate() " + typeof(T).Name);
    }

    public static object Factory = new object();

    public static void Render()
    {
        Console.WriteLine("Render() from BaseTemplate, " + typeof(T).Name);
    }
}

public class Template : BaseTemplate<Template>
{
    static Template()
    {
        Console.WriteLine("Static ctor()");
    }

    public static new void Render()
    {
        Console.WriteLine("Render() from Template.");
        BaseTemplate<Template>.Render();
    }
}

public class Template2 : BaseTemplate<Template2>
{
    static Template2()
    {
        Console.WriteLine("Static ctor() 2");
    }

    public static new void Render()
    {
        Console.WriteLine("Render() from Template2.");
        BaseTemplate<Template2>.Render();
    }

}

Which has this output:

Static ctor()
Render() from Template.
BaseTemplate() Template
Render() from BaseTemplate, Template
Static ctor() 2
Render() from Template2.
BaseTemplate() Template2
Render() from BaseTemplate, Template2
False
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • "Any static members are referenced". In my case I have the `Factory` which is instantiated for the two types, so I would consider it a static member for each inherited types, and they are accessed. – Sébastien Ros - MSFT Aug 14 '17 at 22:29
  • Static members don't inherit the way you are thinking-- there is no instance in which to store a virtual method table. But you can deal with it with an explicit call. Edited my answer. – John Wu Aug 14 '17 at 22:30
  • 2
    @SébastienRos-MSFT Although C# allows you to reference base type static members from derived class name, at IL level this derived type references are replaced by base type. Thus `Template.Factory` is the same as `BaseTemplate – user4003407 Aug 14 '17 at 22:37
  • @PetSerAl this would be my accepted answer if your dared to create one. – Sébastien Ros - MSFT Aug 14 '17 at 23:09