在C#中,强制一个静态字段在其他字段之前被初始化。

huangapple go评论59阅读模式
英文:

Force a static field to be initialized before other field in C#

问题

以下是要翻译的内容:

以下代码尝试记录异常时引发了 NRE 异常:

public static class FooClass
{
    public static string Foo { get; } = CalculateFoo();

    static CalculateFoo()
    {
        try
        {
            DoSomethingThatFails();
        }
        catch (Exception ex)
        {
           _log.Debug(ex); // ----> _log 在此处为 null <----
        }
    }

    static readonly ILog _log = LogManager.GetLogger("FooClass");
}

_log 为 null 的原因是,编译器首先初始化 Foo 属性,然后初始化 _log 成员。

一个可行的解决方案是将 _log 的初始化移到类的顶部。这样,编译器首先初始化 _log,然后初始化 Foo。然而,我们想要避免这样做:在我们的组织中,我们有一些编码规则强制我们按可见性对代码进行排序,因此我们首先将公共内容放在类的顶部,然后是内部内容,最后是私有内容。

是否有任何方法可以告诉编译器初始化顺序?如果没有办法做到这一点,你认为修复这个问题的最佳实践是什么?

编辑

我还尝试添加了一个静态构造函数,但在这种情况下,属性需要在静态构造函数中计算,就在初始化 _log 之后。

英文:

The following code throws a NRE exception trying to log the exception:

public static class FooClass
{
    public static string Foo { get; } = CalculateFoo();

    static CalculateFoo()
    {
        try
        {
            DoSomethingThatFails();
        }
        catch (Exception ex)
        {
           _log.Debug(ex); // ----> _log is null here <----
        }
    }

    static readonly ILog _log = LogManager.GetLogger("FooClass");
}

The reason _log is null, is because the compiler first initialize Foo property, and then it initializes the _log member.

A working solution is moving the _log initialization to the class top. This way the compiler first initializes _log and then Foo. However, we want to avoid that: In our organization, we have some coding rules that force us to sort code by visibility, so we first place public stuff at the class top, then, internal stuff, and finally private stuff.

Is there any way to tell the compiler the initialization order? If there is no way to do that, what would be, in your opinion, the best practices to fix this issue?


EDIT

I also tried to add a static constructor, but in that case the properties need to be calculated in the static constructor, just after initializing the _log.

答案1

得分: 2

// 静态构造函数中初始化,考虑到它们的依赖关系:

public static class FooClass
{

    // 静态构造函数没有可见性,所以你公司的规则在这里并不适用。
    // 无论如何,你可以将其放在类中的任何位置
    static FooClass() {
        _log = LogManager.GetLogger("FooClass");
        Foo = CalculateFoo();
    }

    public static string Foo { get; }

    static string CalculateFoo()
    {
        ...
    }

    static readonly ILog _log;
    
}
英文:

Instead of initialising things inline, you can initialise them in the static constructor, while considering their dependencies:

public static class FooClass
{

    // static constructors don't have a visibility, so your company's rules don't really apply here.
    // either way, you can put this anywhere in the class 
    static FooClass() {
        _log = LogManager.GetLogger("FooClass");
        Foo = CalculateFoo();
    }

    public static string Foo { get; }

    static string CalculateFoo()
    {
        ...
    }

    static readonly ILog _log;
    
}

答案2

得分: 1

> 我还尝试过添加一个静态构造函数,但在这种情况下,属性需要在静态构造函数中计算,就在初始化 _log 之后。

你必须这样做。这里有三个因素:

  1. 当其包含的类是非静态并且被实例化时,或者引用任何静态成员时,将调用静态构造函数。
  2. 在 C# 中,静态成员按文本声明顺序初始化。
  3. 内联初始化器(static Foo = Bar())在静态构造函数之前运行。

这些组合导致了你的问题。所以像 @Sweeper 说的那样做:以你控制的顺序在静态构造函数中初始化所有静态成员。然后你就可以遵循公司的编码准则。除非出现新的情况,证明它们相互矛盾。

所有这些都可以通过在属性的 getter 中不执行 "SomethingThatFails" 来避免。Getter 应该快速且无副作用。如果它们不是这样,它们的逻辑可能应该在一个方法中。

英文:

> I also tried to add a static constructor, but in that case the properties need to be calculated in the static constructor, just after initializing the _log.

You have to. There's three things at play here:

  1. A static constructor is called when its containing class is nonstatic and it is instantiated, or when any static member is referenced.
  2. Static members in C# are initialized in textual declaration order.
  3. Inline initializers (static Foo = Bar()) run before the static constructor.

The combination hereof causes your issue. So do as @Sweeper says: initialize all static members in a static constructor in an order you control. You can then adhere to your company's coding guidelines. Unless a new one pops up and proves them to be contradictory.

All of this can be avoided by not doing "SomethingThatFails" in a property getter. Getters should be quick and side-effect free. If they aren't, their logic should probably be in a method.

huangapple
  • 本文由 发表于 2023年2月14日 19:28:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75447187.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定