11

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?

Domenic
  • 110,262
  • 41
  • 219
  • 271
thecoop
  • 45,220
  • 19
  • 132
  • 189
  • You are directly writing IL rather than compiling C#/VB etc? If so then why don't you compare your IL to that which the C# compiler emits? – David Heffernan Mar 14 '11 at 14:19
  • 1
    You can't define a static constructor on an interface in C#. You can only do it in IL. I'm defining it in exactly the same way as on the class, and that is running as expected. – thecoop Mar 14 '11 at 14:19
  • @David: The C# compiler won't let you implement a static constructor on an interface. – Grant Thomas Mar 14 '11 at 14:20
  • According to the docs you can do it from C++. What IL does that spit out. – David Heffernan Mar 14 '11 at 14:21
  • IL is good choice here because it is explicit. – mgronber Mar 14 '11 at 14:22
  • @David: There's no difference between the IL generated by C++ and what I wrote above. – thecoop Mar 14 '11 at 14:32
  • @thecoop hmm, that's me out of ideas! – David Heffernan Mar 14 '11 at 14:38
  • I've edited the post - adding & accessing a static field to the interface _does_ make the static initializer run. Simply calling a method doesn't. – thecoop Mar 14 '11 at 14:41
  • Ahh, I'm surprised this Jon Skeet answer didn't auto-post itself here: http://stackoverflow.com/questions/610818/what-does-beforefieldinit-flag-do/610837#610837 – Grant Thomas Mar 14 '11 at 14:51
  • 1
    Take a look [here](http://msdn.microsoft.com/en-us/library/3x7c29ta.aspx). It's stated that it runs before any _static_ interface member is accessed. – Jordão Mar 14 '11 at 14:52
  • @Jordão: I verified that it works. The static interface constructor is executed when a static interface method is called. – mgronber Mar 14 '11 at 15:02
  • 2
    @thecoop, I guess that the static constructor is meant to be used to initialize the static fields in the interface. Thereupon there is no need to execute it until someone accesses the static members. – mgronber Mar 14 '11 at 15:11

1 Answers1

11

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
}
Mormegil
  • 7,955
  • 4
  • 42
  • 77
  • But I am accessing a method defined on the interface directly (`callvirt instance int32 IInterface::Method()`), and I am not specifying `beforefieldinit`. This should make the static constructor run, no? – thecoop Mar 14 '11 at 15:05
  • Ah, spotted your link in the comment above. However, that seems to be a C++-specifc thing. Although it does apply to the CLR, I can't find where in the CLR docs this behaviour is specified. – thecoop Mar 14 '11 at 15:07
  • I'm surprised it even mentions it. But that nails it. – Hans Passant Mar 14 '11 at 15:07
  • 1
    @thecoop - As I have said, see [ECMA-335](http://www.ecma-international.org/publications/standards/Ecma-335.htm), Partition I, § 8.9.5. Also, the instance method invocation applies only to value types, reference types are initialized only upon _static_ field access, _static_ method call, or constructor invocation. And, indeed, defining and calling a _static method_ in the interface works and the .cctor gets called. – Mormegil Mar 14 '11 at 15:23
  • @thecoop: I do think that it is specified in Partition I, § 8.9.5 as shown above. It is triggered when the type's static field, static method or constructor is accessed. For value types the static constructor is executed also if instance and virtual methods are called. This means that only static members can trigger the static constructor of the interface. – mgronber Mar 14 '11 at 15:25
  • Ah yes, just spotted it in the spec. Thanks :) – thecoop Mar 14 '11 at 15:36