.net - .NET 中的静态类初始化

标签 .net

我知道我们不初始化静态类。我们可以通过类名来使用它们。但它必须位于静态内存中,以便我们可以使用它们。静态类何时放置在静态内存中?是运行时还是延迟加载?

最佳答案

我怀疑你认为这是懒惰。我不知道你所说的“静态内存”是什么意思,但我认为这是一个关于静态成员的工作原理以及它们如何使用内存的非常可行的问题。静态成员将被放置在“常规内存”中,就像任何实例成员一样。

静态实例在加载时放置到堆上(尽管它是与实例堆不同类型的堆,称为 High Frequency Heap ),但在使用之前不会初始化。根据您拥有的静态成员(即它们的类型,以及这些类型是引用类型还是值类型),这可能是一件大事,也可能是一件小事。

例如,如果您有一个包含 200 个元素的字节数组,则仅在您使用它时才会加载该数组(减去它最初保存的 null)。但如果你有 200 个字节的元素,每个元素都会在开始时放入内存中。

也就是说,在使用之前,ClassA会比ClassB占用更少的内存,但在使用过程中,它们会相似。当然,不完全相同,但你可以想象我想要表达的观点。

static class ClassA
{
    public static byte[] bytes = new byte[] { 1, 12 };
}

static class ClassB
{
    public static byte ByteA = 1;
    public static byte ByteB = 12;
}

那是因为在ClassA中,bytes的初始值为null,但是在ClassB中,有从进程开始就存储的两个值,这两个值恰好都是0。您可以想象这会如何影响其他引用类型字段与值类型字段。

当 GC 运行时,另一个区别就会发挥作用。如果我没有弄错的话(尽管我不相信这是规范的行为),静态成员在进程终止之前不会被垃圾收集。但除此之外,您可以(松散地)将它们视为该类的简单的、编译器强制的单例实例。


确定这种“延迟加载”值的最简单方法是在静态构造函数中放置一个副作用调用。比如,

public class Foo
{
    static Foo()
    {
        Console.WriteLine("In static constructor!");
    }

    public static void Bar()
    {
        Console.WriteLine("In Bar!");
    }
}

放在小型控制台应用程序的上下文中,您可以轻松确定何时调用构造函数,并推断这也是何时将静态成员放入内存中。

void Main(string[] args)
{
    Console.WriteLine("First line!");
    Foo.Bar();
    Console.WriteLine("Last line!");
}

这个程序outputs :

First line!
In static constructor!
In Bar!
Last line!

您还可以在 C# 5.0 规范(§10.12 静态构造函数)中找到此信息,

The static constructor for a closed class type executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • An instance of the class type is created.
  • Any of the static members of the class type are referenced.

用外行人的话来说,静态成员在您第一次使用静态类时被初始化。如果从未使用过,则它们永远不会被初始化。


为了阐明我的观点,让我们看一下最后一对方法。

static class Foo
{
    public static int X = 2;
    public static byte[] Bytes = new byte[] { 24, 66 };
}
class Bar
{
    public int X = 2;
    public byte[] Bytes = new byte[] { 24, 66 };
}

单步执行(在 IL 级别),与这些类的交互将非常不同。

考虑一个方法:

void Main(string[] args)
{
    // Foo is put into memory when the process spins up:
    //     X : 0
    //     Bytes : null

    Console.Write(Foo.X); // During this line, Foo is initialized
                          //     X : 2
                          //     Bytes : 24, 66

    Console.Write(Foo.X); // Nothing happens

    Bar b = new Bar(); // During this line, Bar is put into memory, then initialized
                       //     X : 0, then "instantaneously" becomes 2
                       //     Bytes : null, then "instantaneously" becomes 24, 66

    Console.Write(b.X); // Nothing happens

    // Eventually, b will be cleared from memory by the garbage collector
    // but Foo will not be, until the program closes. This sample is much
    // too small to display that difference.
}

关于.net - .NET 中的静态类初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28470664/

相关文章:

.net - asp.net Web 应用程序 Azure 应用服务远程调试中的问题

c# - 关于如何使用类似插件的体系结构实现 c# 主机应用程序的问题

c# - 创建文件而不打开/锁定它?

c# - 如何从字符串中分离数据

c# - 设置升级后如何删除以前的用户设置?

.net - 运行exe后如何返回退出代码?

.net - NET的良好解析器生成器(认为lex/yacc或antlr)?仅建立时间?

c# - ASP.NET MVC : Controller or ControllerBase? 中 Controller 的内置基类

.net - MEF 最佳实践是什么?

c# - 具有 SHA-1 的 .NET RSACryptoServiceProvider 的 Java 等价物