82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

这是我假设的顺序

  1. 静态构造函数的开始
  2. 静态构造函数结束
  3. 主线开始
  4. MyMethod 的开始
  5. 主线结束

现在在任何情况下,如果 4 将在 2 之前开始,我就完蛋了。是否可以?

4

10 回答 10

220

你在这里只问了一个问题,但有十几个问题你应该问,所以我会一一回答。

这是我假设的顺序

  1. 类构造函数的开始(也称为cctor
  2. cctor 结束
  3. 主要的开始
  4. MyMethod 的开始

它是否正确?

不,正确的顺序是:

  1. 程序的 cctor 的开始,如果有的话。那没有。
  2. 程序的 cctor 结束,如果有的话。那没有。
  3. 主要开始
  4. MyClass 的 cctor 开始
  5. MyClass 的 cctor 结束
  6. MyClass.MyMethod 的开始

如果有一个静态字段初始化器怎么办?

在某些情况下,允许 CLR 更改静态字段初始化程序的运行顺序。有关详细信息,请参见 Jon 的页面:

静态构造器和类型初始化器的区别

是否有可能MyMethod在该类的 cctor 完成之前调用静态方法?

是的。如果 cctor 本身调用 MyMethod,那么显然 MyMethod 将在 cctor 完成之前被调用。

cctor 不调用 MyMethod。是否有可能MyMethod在 MyClass 的 cctor 完成之前调用静态方法?

是的。如果 cctor 使用其 cctor 调用 MyMethod 的另一个类型,则 MyMethod 将在 MyClass cctor 完成之前被调用。

没有 cctor 直接或间接调用 MyMethod!现在有可能MyMethod在 MyClass 的 cctor 完成之前调用静态方法吗?

不。

即使涉及多个线程,这仍然是真的吗?

是的。在可以在任何线程上调用静态方法之前,cctor 将在一个线程上完成。

可以多次调用 cctor 吗?假设两个线程都导致 cctor 运行。

无论涉及多少线程,都保证最多调用一次 cctor。如果两个线程“同时”调用 MyMethod,那么它们就会竞争。其中一个输掉比赛并阻塞,直到 MyClass cctor 在获胜线程上完成。

丢失的线程阻塞直到 cctor 完成?真的吗?

真的。

那么,如果获胜线程上的 cctor 调用了阻塞先前被失败线程占用的锁的代码呢?

然后你有一个经典的锁顺序反转条件。你的程序死锁了。永远。

这似乎很危险。我怎样才能避免死锁?

如果你这样做时感到疼痛,那就停止这样做永远不要做会阻碍 cctor 的事情。

依靠 cctor 初始化语义来强制执行复杂的安全要求是一个好主意吗?让一个负责用户交互的 cctor 是个好主意吗?

两者都不是好主意。我的建议是,您应该找到一种不同的方法来确保满足您的方法的影响安全的先决条件。

于 2012-02-22T16:38:46.200 回答
24

根据MSDN,一个静态构造函数:

在创建第一个实例或引用任何静态成员之前,会自动调用静态构造函数来初始化类。

所以静态构造函数会在调用静态方法之前MyClass.MyMethod()被调用(当然假设在静态构造或静态字段初始化期间 也不会调用)。

现在,如果您在其中做任何异步操作static constructor,那么同步它就是您的工作。

于 2012-02-22T16:14:23.873 回答
11

#3 实际上是#1:静态初始化直到第一次使用它所属的类才开始。

MyMethod从静态构造函数或静态初始化块调用if 是可能的。如果您不MyMethod直接或间接从静态构造函数调用,则应该没问题。

于 2012-02-22T16:13:18.507 回答
9

文档(强调我的):

在创建第一个实例或引用任何静态成员之前,会自动调用静态构造函数来初始化类 。

于 2012-02-22T16:15:05.027 回答
2

您可以保证 4 总是在 2 之后(如果您不从静态方法创建类的实例),但是对于 1 和 3 则不是这样。

于 2012-02-22T16:15:20.877 回答
2

在执行 mymethod 之前将调用静态构造函数。但是,如果您在 2 之前调用 4 就搞砸了,那么我建议您重新考虑您的设计。无论如何都不应该在静态构造函数中做复杂的事情。

于 2012-02-22T16:15:22.593 回答
2

CLR 保证静态构造函数在访问任何静态成员之前运行。但是,您的设计有点臭。做这样的事情会更直接:

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

根据您的设计,如果身份验证失败,阻止 MyMethod 运行的唯一方法是引发异常。

于 2012-02-22T16:16:51.750 回答
2

确保静态类的构造函数在其任何方法执行之前已被调用。例子:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

输出:

按回车

// 回车后

你好进来...

你好呀!

你好呀!

于 2012-02-22T16:18:33.233 回答
1

这是事情发生的实际顺序:

  1. 开始Main
  2. 静态MyClass构造函数的开始
  3. 静态MyClass构造函数结束
  4. 开始MyMethod
  5. 结束Main
于 2012-03-03T02:47:27.813 回答
0

或者您可以在调试器中单步执行。

于 2012-02-29T19:49:48.077 回答