Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static constructor on a .NET interface is not run

You can define a static constructor on an interface in .NET in IL. However, if you do so, the static constructor is not run when you run a method on the interface:

.method public static void Main() {
    .entrypoint    
    .locals init ( class IInterface cls1 )

    // InterfaceClass static constructor is run
    newobj instance void InterfaceClass::.ctor()
    stloc.0
    ldloc.0
    // IInterface static constructor is not run!!!!!
    callvirt instance int32 IInterface::Method()
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
}

.class public interface IInterface {
    .method private static specialname rtspecialname void .cctor() {
        ldstr "Interface static cctor"
        call void [mscorlib]System.Console::WriteLine(string)
        ret
    }

    .method public abstract virtual instance int32 Method() {}
}

.class public InterfaceClass implements IInterface {

    .method private static specialname rtspecialname void .cctor() {
        ldstr "Class static cctor"
        call void [mscorlib]System.Console::WriteLine(string)
        ret
    }

    .method public specialname rtspecialname instance void .ctor() {
        ldarg.0
        call instance void [mscorlib]System.Object::.ctor()
        ret
    }

    .method public virtual instance int32 Method() {
        ldc.i4.s 42
        ret
    }
}

What's going on here? The CLR spec (Partition II, 10.5.3.1) says that when type initializers are executed is specified in Partition I, but I cannot find any reference in Partition I to type initializer execution.

EDIT:

I can get the interface static intitializer to run, but only by adding a static field to the interface, and accessing that field somewhere in the code, even if the field isn't actually assigned in the static constructor. So it seems that calling a method on an interface does not make the static constructor run, but accessing a field does. Why is this the case? And where is this mentioned in the spec?

like image 627
thecoop Avatar asked Mar 14 '11 14:03

thecoop


1 Answers

It seems to me that even though you can define a .cctor on an interface in CLI, it is kind of useless. Partition I, § 8.9.5 states:

If marked BeforeFieldInit then the type‘s initializer method is executed at, or sometime before, first access to any static field defined for that type. If not marked BeforeFieldInit then that type‘s initializer method is executed at (i.e., is triggered by): first access to any static field of that type, or first invocation of any static method of that type, or first invocation of any instance or virtual method of that type if it is a value type or first invocation of any constructor for that type. Execution of any type's initializer method will not trigger automatic execution of any initializer methods defined by its base type, nor of any interfaces that the type implements

(emphasis mine) Which means the type initializer on an interface does not get called automatically at all. If you want it to be called, you (IMHO) need to call it explicitly in all implementing classes, like this:

.method private static specialname rtspecialname void .cctor() {
    ldtoken IInterface
    callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle()
    call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::RunClassConstructor(valuetype [mscorlib]System.RuntimeTypeHandle)
    ldstr "Class static cctor"
    call void [mscorlib]System.Console::WriteLine(string)
    ret
}
like image 178
Mormegil Avatar answered Oct 08 '22 02:10

Mormegil