请向我解释静态构造函数的使用。我们为什么以及何时创建一个静态构造函数,是否可以重载一个?
8 回答
不,你不能超载它;静态构造函数可用于初始化与类型关联的任何静态字段(或任何其他按类型操作) - 特别适用于将所需的配置数据读入只读字段等。
它在第一次需要时由运行时自动运行(确切的规则很复杂(参见“beforefieldinit”),并且在 CLR2 和 CLR4 之间进行了微妙的更改)。除非你滥用反射,否则它保证最多运行一次(即使两个线程同时到达)。
静态构造函数用于初始化任何静态数据,或执行只需要执行一次的特定操作。在创建第一个实例或引用任何静态成员之前自动调用它。
静态构造函数具有以下属性:
静态构造函数不采用访问修饰符或具有参数。
在创建第一个实例或引用任何静态成员之前,会自动调用静态构造函数来初始化类。
不能直接调用静态构造函数。
用户无法控制何时在程序中执行静态构造函数。
静态构造函数的典型用途是当类使用日志文件并且构造函数用于将条目写入该文件时。
LoadLibrary
当构造函数可以调用方法时,静态构造函数在为非托管代码创建包装类时也很有用。
当您有相互依赖的静态字段以使初始化顺序很重要时,静态构造函数也非常有用。如果您通过更改字段顺序的格式化程序/美化器运行代码,那么您可能会发现自己在未预料到的地方出现空值。
示例:假设我们有这个类:
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/ ”,因为urlFragment
在设置时尚未初始化fullUrl
。不好。因此,您添加一个静态构造函数来处理初始化:
class ScopeMonitor
{
static string firstPart= "http://www.example.com/";
static string fullUrl;
static string urlFragment = "foo/bar";
static ScopeMonitor()
{
fullUrl= firstPart + urlFragment;
}
}
现在,无论字段的顺序如何,初始化总是正确的。
1.它只能访问类的静态成员。
原因:非静态成员特定于对象实例。如果允许静态构造函数作用于非静态成员,它将反映所有对象实例的变化,这是不切实际的。
2.静态构造函数中不应该有参数。
原因:既然,它将被CLR调用,没有人可以将参数传递给它。3.只允许一个静态构造函数。
原因:重载需要两种方法在方法/构造函数定义方面有所不同,这在静态构造函数中是不可能的。
4.它不应该有访问修饰符。
原因:同样的原因是对静态构造函数的调用是由 CLR 而不是由对象进行的,不需要对其进行访问修饰符
您可以使用静态构造函数来初始化静态字段。在使用这些字段之前,它会在不确定的时间运行。Microsoft 的文档和许多开发人员警告说,类型上的静态构造函数会带来大量开销。
最好避免使用静态构造函数以获得最佳性能。
更新:您不能在同一个类中使用多个静态构造函数,但是您可以将其他实例构造函数与(最多)一个静态构造函数一起使用。
为什么以及何时创建静态构造函数...?
使用静态构造函数的一个具体原因是创建一个“超级枚举”类。这是一个(简单,人为的)示例:
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
只调用创建的类的第一个实例。并用于执行在类的生命周期中只需要执行一次的特定操作。
静态构造函数(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) 代码。
- 静态构造函数的存在阻止了添加外部参照:System.Reflection.TypeAttributes.BeforeFieldInit 类型属性。这限制了运行时优化。
- 声明为的字段
static readonly
只能作为其声明的一部分或在静态构造函数中分配。当不需要显式静态构造函数时,在声明时初始化静态字段而不是通过静态构造函数来进行更好的运行时优化。 - 运行时在单个应用程序域中调用静态构造函数不超过一次。该调用是在基于类的特定类型的锁定区域中进行的。静态构造函数的主体中不需要额外的锁定机制。为了避免死锁的风险,不要在静态构造函数和初始化程序中阻塞当前线程。例如,不要等待任务、线程、等待句柄或事件,不要获取锁,也不要执行阻塞并行操作,如并行循环
Parallel.Invoke
和并行 LINQ 查询。
[!Note] 虽然不能直接访问,但应记录显式静态构造函数的存在,以帮助解决初始化异常。
用法
- 静态构造函数的典型用途是当类使用日志文件并且构造函数用于将条目写入该文件时。
LoadLibrary
当构造函数可以调用方法时,静态构造函数在为非托管代码创建包装类时也很有用。- 静态构造函数也是对无法在编译时通过类型参数约束检查的类型参数强制执行运行时检查的方便场所。