.NET 接口上的静态构造函数未运行

发布于 2024-10-21 13:53:50 字数 1512 浏览 7 评论 0原文

您可以在 IL 中的 .NET 接口上定义静态构造函数。但是,如果这样做,当您在接口上运行方法时,静态构造函数不会运行:

.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
    }
}

这是怎么回事? CLR 规范(分区 II,10.5.3.1)表示,类型初始值设定项何时执行是在分区 I 中指定的,但我在分区 I 中找不到任何对类型初始值设定项执行的引用。

编辑:

可以运行接口静态初始化程序,但只能通过向接口添加静态字段并在代码中的某处访问该字段,即使该字段实际上并未在静态构造函数。因此,似乎在接口上调用方法不会使静态构造函数运行,但访问字段却可以。为什么会这样呢?规范中哪里提到了这一点?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

阪姬 2024-10-28 13:53:50

在我看来,即使您可以在 CLI 中的接口上定义 .cctor,它也是无用的。第 I 部分第 8.9.5 条规定:

如果标记为 BeforeFieldInit,则在首次访问为该类型定义的任何静态字段时或之前执行该类型的初始化方法。如果未标记 BeforeFieldInit,则该类型的初始化方法将在以下位置执行(即触发):
首先访问该类型的任何静态字段,或者
第一次调用该类型的任何静态方法,或者
第一次调用该类型的任何实例或虚拟方法(如果它是值类型)或
第一次调用该类型的任何构造函数。
执行任何类型的初始化方法都不会触发其基类型定义的任何初始化方法的自动执行,也不会触发该类型实现的任何接口的自动执行

(强调我的)这意味着接口上的类型初始值设定项不会触发完全被自动调用。如果您希望调用它,您(恕我直言)需要在所有实现类中显式调用它,如下所示:

.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
}

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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文