C#具有一个默认开启的代码分析规则:[CA1810]Initialize reference type static fields inline

2025-12-13 0 652

C#具有一个默认开启的代码分析规则:[CA1810]Initialize reference type static fields inline,推荐我们以内联的方式初始化静态字段,而不是将初始化放在静态构造函数中。

一、两种初始化的性能差异
二、beforefieldinit标记
三、静态构造函数执行的时机
四、关于“All-Zero”结构体
五、RuntimeHelpers.RunClassConstructor方法

一、两种初始化的性能差异

CA1810这一规则与性能有关,我们可以利用如下这段简单的代码来演示两种初始化的性能差异。Foo和Bar这两个类的静态字段都定义了一个名为_value的静态字段,它们均通过调用静态方法Initialize返回的值进行初始化。不同的是Foo以内联(inline)赋值的方法进行初始化,而Bar则将初始化操作定义在静态构造函数中。假设Initialize方法是一个相对耗时的操作,我们利用Program的_initialized字段判断该方法是否被调用。

static class Program
{
  private static bool _initialized;
  static void Main()
  {
    Foo.Invoke();
    Debug.Assert(_initialized == false);

    Bar.Invoke();
    Debug.Assert(_initialized == true);
  }
  private static int Initialize()
  {
    _initialized = true;
    return 123;
  }
  public class Foo
  {
    private readonly static int _value = Initialize();
    public static int Value => _value;
    public static void Invoke() { }
  }
  public class Bar
  {
    private readonly static int _value;
    public static int Value => _value;
    static Bar() => _value = Initialize();
    public static void Invoke() { }
  }
}

从我们给出的调用断言可以确定,当我们调用Foo的静态方法Invoke时,它的静态字段_value并没有初始化;但是当我们调用Bar的Invoke方法时,Initialize方法会率先被调用来初始化静态字段。从这个例子来说,由于整个应用并没有使用到Foo和Bar的静态字段,所以针对它们的初始化是没有必要的。所以我们说以内联方式对静态字段进行初始化的Foo具有更好的性能。

二、beforefieldinit标记

对于Foo和Bar这两个类型表现出来的不同行为,我们可以试着从IL代码层面寻找答案。如下所示的两段IL代码分别来源于Foo和Bar,我们可以看到虽然Foo类中没有显式定义静态构造函数,但是编译器会创建一个默认的静态构造函数,针对静态字段的初始化就放在这里。我们可以进一步看出,自动生成的这个静态构造函数和我们自己写的并没有本质的不同。两个类型之间的差异并没有体现在静态构造函数上,而是在于:没有显式定义静态构造函数的Foo类型上具有一个beforefieldinit标记。

	

		

.class public auto ansi beforefieldinit Foo extends [System.Runtime]System.Object { .field private static initonly int32 _value .method private hidebysig specialname rtspecialname static void .cctor () cil managed { .maxstack 8 IL_0000: call int32 Utility::Initialize() IL_0005: stsfld int32 Foo::_value IL_000a: ret }

… }

.class public auto ansi Bar
	extends [System.Runtime]System.Object
{

	.field private static initonly int32 _value	
	.method private hidebysig specialname rtspecialname static void .cctor () cil managed
	{
		.maxstack 8

		IL_0000: call int32 Utility::Initialize()
		IL_0005: stsfld int32 Bar::_value
		IL_000a: ret
	} 
	
} 

三、静态构造函数执行的时机

从Foo和Bar的IL代码可以看出,针对它们静态字段的初始化都放在静态构造函数中。但是当我们调用一个并不涉及类型静态字段的Invoke方法时,定义在Foo中的静态构造函数会自动执行,但是定义在Bar中的则不会,由此可以看出一个类型的静态构造函数的执行时机与类型是否具有beforefieldinit标记有关。具体规则如下,这一个规则直接定义在CLI标准ECMA-335中,静态构造函数在此标准中被称为类型初始化器(Type Initializer)或者.cctor。

  • 具有beforefieldinit标记:静态构造函数会在第一次读取任何一个静态字段之前自动执行,这相当于一种Lazy loading的模式;
  • 不具有beforefieldinit标记:静态构造函数会在如下场景下自动执行:
    • 第一次读取任何一个静态字段之前;
    • 第一个执行任何一个静态方法之前;
    • 引用类型:第一次调用构造函数之前;
    • 值类型:第一次调用实例方法;

由于beforefieldinit标记只有在没有显式定义静态构造函数的情况下才会被添加,所以我们自行定义的专门用来初始化静态字段的静态构造函数是完全没有必要的。不但没有必要,还可能带来性能问题,应该改成以内联的形式对静态字段进行初始化。

四、关于“All-Zero”结构体

如果我们在一个结构体中显式定义了一个静态构造函数,当我们调用其构造函数之前,静态构造函数会自动执行。

public class Program
{ private static bool _initialized= false; static void Main()
    {
        var foobar = new Foobar(1, 2);
        Debug.Assert(_initialized == true);
    } public struct Foobar
    { static Foobar() => _initialized = true; public Foobar(int foo, int bar)
        {
            Foo = foo;
            Bar = bar;
        } public int Foo { get; } public int Bar { get; }
    }
}

倘若按照如下的方式利用default关键字得到一个所有字段为“零”的默认结构体(all-zero structure),我们显式定义的静态构造函数是不会执行的。

public class Program
{ private static bool _initialized = false; static void Main()
    {
        Foobar foobar = default;
        Debug.Assert(foobar.Foo == 0);
        Debug.Assert(foobar.Bar == 0);
        Debug.Assert(_initialized == false);
    }
    ...
}

五、RuntimeHelpers.RunClassConstructor方法

如果我们要确保某个类型的静态构造函数已经被显式调用,可以执行RuntimeHelpers.RunClassConstructor方法,它的参数为目标类型的TypeHandle。

	

		

public class Program { private static bool _initialized = false; static void Main() { RuntimeHelpers.RunClassConstructor(typeof(Foobar).TypeHandle); Debug.Assert(_initialized ); }

… }

由于类型的静态构造函数只会被执行一次,所以多次RuntimeHelpers.RunClassConstructor并不会导致静态函数的重复执行。

public class Program
{ private static bool _initialized = false; static void Main()
    {
        RuntimeHelpers.RunClassConstructor(typeof(Foobar).TypeHandle);
        Debug.Assert(_initialized == true);
        _typeInitializerInvoked = false;
        Debug.Assert(_initialized == false);
    }
   ...
}
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 C#具有一个默认开启的代码分析规则:[CA1810]Initialize reference type static fields inline https://www.zuozi.net/36344.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务