静态构造函数的用途是什么?

请给我解释一下静态构造函数的用法。为什么,什么时候我们要创建一个静态构造函数,它是否可能重载一个?

216792 次浏览

静态构造函数(c#编程指南):

静态构造函数用于 初始化任何静态数据,或为 执行需要的特定操作 只执行一次。叫做 自动在第一个之前 实例或任何静态 成员被引用 静态构造函数有以下内容 属性:< / p >

  • 静态构造函数不带访问修饰符或参数。

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

  • 不能直接调用静态构造函数。

  • 用户无法控制静态构造函数何时在程序中执行。

  • 静态构造函数的典型用法是类使用日志文件,构造函数用于向该文件写入条目。

  • 当构造函数可以调用LoadLibrary方法时,静态构造函数在为非托管代码创建包装器类时也很有用。

不,你不能让它过载;静态构造函数对于初始化与类型相关的任何静态字段(或任何其他每种类型的操作)非常有用——特别是对于将所需的配置数据读入只读字段等非常有用。

它在第一次需要时由运行时自动运行(那里的确切规则很复杂(参见“beforefieldinit”),并且在CLR2和CLR4之间有微妙的变化)。除非你滥用反射,它保证运行最多一次(即使两个线程同时到达)。

你可以使用静态构造函数初始化静态字段。在使用这些字段之前,它在一个不确定的时间运行。微软的文档和许多开发人员警告说,类型上的静态构造函数会带来巨大的开销。为了获得最佳性能,最好避免使用静态构造函数。< br > 你不能在同一个类中使用多个静态构造函数,但是你可以使用(最多)一个静态构造函数的其他实例构造函数。

1.它只能访问类的静态成员。

原因:非静态成员特定于对象实例。如果允许静态构造函数作用于非静态成员,它将反映所有对象实例中的变化,这是不切实际的。

2.在静态构造函数中不应该有参数。

原因:因为它将被CLR调用,所以没有人可以将参数传递给它。 3.只有一个静态构造函数被允许

原因:重载需要两个方法在方法/构造函数定义方面有所不同,这在静态构造函数中是不可能的。

4.它不应该有访问修饰符。

原因:同样的原因是对静态构造函数的调用是由CLR而不是由对象进行的,不需要对它有访问修饰符

当静态字段相互依赖,初始化顺序很重要时,静态构造函数也非常有用。如果您通过一个改变字段顺序的格式化器/美化器运行代码,那么您可能会发现自己在意想不到的地方得到了空值。

示例:假设我们有这样一个类:

class ScopeMonitor
{
static string urlFragment = "foo/bar";
static string firstPart= "http://www.example.com/";
static string fullUrl= firstPart + urlFragment;
}

当你访问fullUr时,它将是"http://www.example.com/foo/bar"。

几个月后,您将清理代码并按字母顺序排列字段(假设它们是一个更大的列表的一部分,因此您没有注意到问题)。你有:

class ScopeMonitor
{
static string firstPart= "http://www.example.com/";
static string fullUrl= firstPart + urlFragment;
static string urlFragment = "foo/bar";
}

你的fullUrl值现在只是"http://www.example.com/",因为在设置fullUrl时,urlFragment还没有初始化。不好的。所以,你添加一个静态构造函数来处理初始化:

class ScopeMonitor
{
static string firstPart= "http://www.example.com/";
static string fullUrl;
static string urlFragment = "foo/bar";


static ScopeMonitor()
{
fullUrl= firstPart + urlFragment;


}
}

现在,不管字段的顺序是什么,初始化总是正确的。

为什么以及什么时候创建静态构造函数?

使用静态构造函数的一个具体的原因是创建一个“超级枚举”类。这里有一个(简单的,做作的)例子:

public class Animals
{
private readonly string _description;
private readonly string _speciesBinomialName;


public string Description { get { return _description; } }
public string SpeciesBinomialName { get { return _speciesBinomialName; } }


private Animals(string description, string speciesBinomialName)
{
_description = description;
_speciesBinomialName = speciesBinomialName;
}


private static readonly Animals _dog;
private static readonly Animals _cat;
private static readonly Animals _boaConstrictor;


public static Animals Dog { get { return _dog; } }
public static Animals Cat { get { return _cat; } }
public static Animals BoaConstrictor { get { return _boaConstrictor; } }


static Animals()
{
_dog = new Animals("Man's best friend", "Canis familiaris");
_cat = new Animals("Small, typically furry, killer", "Felis catus");
_boaConstrictor = new Animals("Large, heavy-bodied snake", "Boa constrictor");
}
}

你会非常相似地(在语法外观上)使用任何其他枚举:

Animals.Dog

与常规enum相比,它的优点是可以很容易地封装相关信息。一个缺点是你不能在switch语句中使用这些值(因为它需要常量值)。

Static constructor只调用创建的类的第一个实例。并用于执行在类的生命周期中只需要执行一次的特定操作。

微软文档 https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors < / p >

静态构造函数(c#编程指南)

静态构造函数用于初始化任何静态数据,或执行只需要执行一次的特定操作。在创建第一个实例或引用任何静态成员之前自动调用它。

class SimpleClass
{
// Static variable that must be initialized at run time.
static readonly long baseline;


// Static constructor is called at most one time, before any
// instance constructor is invoked or member is accessed.
static SimpleClass()
{
baseline = DateTime.Now.Ticks;
}
}

讲话

静态构造函数有以下属性:

  • 静态构造函数不接受访问修饰符或参数。
  • 一个类或结构只能有一个静态构造函数。
  • 静态构造函数不能被继承或重载。
  • 静态构造函数不能直接调用,只能由公共语言运行库(CLR)调用。它是自动调用的。
  • 用户无法控制静态构造函数何时在程序中执行。
  • 静态构造函数被自动调用。它在创建第一个实例或引用该类中声明的任何静态成员(不是它的基类)之前初始化该类。静态构造函数在实例构造函数之前运行。当调用赋值给事件或委托的静态方法时调用类型的静态构造函数,而不是赋值时调用类型的静态构造函数。如果静态构造函数的类中存在静态字段变量初始化式,则它们将按照它们在类声明中出现的文本顺序执行。初始化式在执行静态构造函数之前立即运行。
  • 如果你没有提供一个静态构造函数来初始化静态字段,所有的静态字段都被初始化为它们的默认值,如c#类型的默认值所示。
  • 如果静态构造函数抛出异常,运行时不会第二次调用它,并且该类型将在应用程序域的生命周期内保持未初始化状态。最常见的是一个xref:System。当静态构造函数无法实例化类型或在静态构造函数中发生未处理的异常时,抛出TypeInitializationException异常。对于源代码中没有显式定义的静态构造函数,排除故障可能需要检查中间语言(IL)代码。
  • 静态构造函数的存在阻止了添加xref:System.Reflection.TypeAttributes。BeforeFieldInit类型属性。这限制了运行时优化。
  • 声明为static readonly的字段只能作为其声明的一部分或在静态构造函数中赋值。当不需要显式静态构造函数时,在声明时初始化静态字段,而不是通过静态构造函数来实现更好的运行时优化。
  • 运行时在单个应用程序域中调用静态构造函数不超过一次。该调用是基于类的特定类型在锁定区域中进行的。在静态构造函数体中不需要额外的锁定机制。为了避免死锁的风险,不要在静态构造函数和初始化器中阻塞当前线程。例如,不要等待任务、线程、等待句柄或事件,不要获取锁,不要执行阻塞并行操作,如并行循环、Parallel.Invoke和并行LINQ查询。
< p > [!请注意) 虽然不能直接访问,但显式静态构造函数的存在应该被记录下来,以帮助排除初始化异常

使用

  • 静态构造函数的典型用法是类使用日志文件,构造函数用于向该文件写入条目。
  • 当构造函数可以调用LoadLibrary方法时,静态构造函数在为非托管代码创建包装器类时也很有用。
  • 静态构造函数也是一个方便的地方,可以强制执行在编译时不能通过类型参数约束进行检查的类型参数的运行时检查。