- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
C#具有一个默认开启的代码分析规则:[ CA1810 ]Initialize reference type static fields inline,推荐我们以内联的方式初始化静态字段,而不是将初始化放在静态构造函数中.
1、两种初始化的性能差异 2、beforefieldinit标记 3、静态构造函数执行的时机 4、关于“All-Zero”结构体 5、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具有更好的性能.
对于Foo和Bar这两个类型表现出来的不同行为,我们可以试着从IL代码层面寻找答案。如下所示的两段IL代码分别来源于Foo和Bar,我们可以看到虽然Foo类中没有显式定义静态构造函数,但是编译器会创建一个默认的静态构造函数,针对静态字段的初始化就放在这里。我们可以进一步看出,自动生成的这个静态构造函数和我们自己写的并没有本质的不同。两个类型之间的差异并没有体现在静态构造函数上,而是在于: 没有显式定义静态构造函数 的Foo类型上具有一个beforefieldinit标记.
. class public auto ansi beforefieldinit Foo extends [System.Runtime]System.Object { .field private static initonly int 32 _value .method private hidebysig specialname rtspecialname static void .cctor () cil managed { .maxstack 8 IL_0000: call int 32 Program::Initialize() IL_0005: stsfld int 32 Foo::_value IL_000a: ret } 。
… } 。
. class public auto ansi Bar extends [System.Runtime]System.Object { .field private static initonly int 32 _value .method private hidebysig specialname rtspecialname static void .cctor () cil managed { .maxstack 8 IL_0000: call int 32 Program::Initialize() IL_0005: stsfld int 32 Bar::_value IL_000a: ret } }
从Foo和Bar的IL代码可以看出,针对它们静态字段的初始化都放在静态构造函数中。但是当我们调用一个并不涉及类型静态字段的Invoke方法时,定义在Foo中的静态构造函数会自动执行,但是定义在Bar中的则不会,由此可以看出一个类型的静态构造函数的执行时机与类型是否具有beforefieldinit标记有关。具体规则如下,这一个规则直接定义在CLI标准 ECMA-335 中,静态构造函数在此标准中被称为类型初始化器(Type Initializer)或者.cctor.
由于beforefieldinit标记只有在没有显式定义静态构造函数的情况下才会被添加,所以我们自行定义的专门用来初始化静态字段的静态构造函数是完全没有必要的。不但没有必要,还可能带来性能问题,应该改成以内联的形式对静态字段进行初始化.
如果我们在一个结构体中显式定义了一个静态构造函数,当我们调用其构造函数之前,静态构造函数会自动执行.
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方法,它的参数为目标类型的TypeHandle.
public class Program { private static bool _initialized = false ; static void Main() { RuntimeHelpers.RunClassConstructor( typeof (Foobar).TypeHandle); Debug.Assert(_initialized == true ); } 。
… } 。
由于类型的静态构造函数只会被执行一次,所以多次RuntimeHelpers.RunClassConstructor并不会导致静态函数的重复执行.
public class Program { private static bool _initialized = false ; static void Main() { RuntimeHelpers.RunClassConstructor( typeof (Foobar).TypeHandle); Debug.Assert(_initialized == true ); _typeInitializerInvoked = false ; RuntimeHelpers.RunClassConstructor( typeof (Foobar).TypeHandle); Debug.Assert(_initialized == false ); } ... } 。
最后此篇关于为什么应该尽可能避免在静态构造函数中初始化静态字段?的文章就讲到这里了,如果你想了解更多关于为什么应该尽可能避免在静态构造函数中初始化静态字段?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我们已经有一个使用 AnyEvent 的库。它在内部使用 AnyEvent,并最终返回一个值(同步 - 不使用回调)。有什么方法可以将这个库与 Mojolicious 一起使用吗? 它的作用如下: #
我想从 XSD 文件生成带有 JAXB 的 Java 类。 问题是,我总是得到一些像这样的类(删除了命名空间): public static class Action { @X
我有一个关于 html 输入标签或 primefaces p:input 的问题。为什么光标总是自动跳转到输入字段。我的页面高度很高,因此您需要向下滚动。输入字段位于页面末尾,光标自动跳转(加载)到页
我今天在考虑面向对象设计,我想知道是否应该避免 if 语句。我的想法是,在任何需要 if 语句的情况下,您都可以简单地创建两个实现相同方法的对象。这两个方法实现只是原始 if 语句的两个可能的分支。
String graphNameUsed = graphName.getName(); if (graphType.equals("All") || graphType.equals(
我有一张友谊 table CREATE TABLE IF NOT EXISTS `friendList` ( `id` int(10) NOT NULL, `id_friend` int(10
上下文 Debian 64。Core 2 二人组。 摆弄循环。我使用了同一循环的不同变体,但我希望尽可能避免条件分支。 但是,即使我认为它也很难被击败。 我考虑过 SSE 或位移位,但它仍然需要跳转(
我最近在 Java 中创建了一个方法来获取字符串的排列,但是当字符串太长时它会抛出这个错误:java.lang.OutOfMemoryError: Java heap space我确信该方法是有效的,
我正在使用 (C++) 库,其中需要使用流初始化对象。库提供的示例代码使用此代码: // Declare the input stream HfstInputStream *in = NULL; tr
我有一个 SQL 查询,我在 WHERE 子句中使用子查询。然后我需要再次使用相同的子查询将其与不同的列进行比较。 我假设没有办法在子查询之外访问“emp_education_list li”? 我猜
我了解到在 GUI 线程上不允许进行网络操作。对我来说还可以。但是为什么在 Dialog 按钮点击回调上使用这段代码仍然会产生 NetworkOnMainThreadException ? new T
有没有办法避免在函数重定向中使用 if 和硬编码字符串,想法是接收一个字符串并调用适当的函数,可能使用模板/元编程.. #include #include void account() {
我正在尝试避免客户端出现 TIME_WAIT。我连接然后设置 O_NONBLOCK 和 SO_REUSEADDR。我调用 read 直到它返回 0。当 read 返回 0 时,errno 也为 0。我
我正在开发 C++ Qt 应用程序。为了在应用程序或其连接的设备出现故障时帮助用户,程序导出所有内部设置并将它们存储在一个普通文件(目前为 csv)中。然后将此文件发送到公司(例如通过邮件)。 为避免
我有一组具有公共(public)父类(super class)的 POJO。这些存储在 superclass 类型的二维数组中。现在,我想从数组中获取一个对象并使用子类 的方法。这意味着我必须将它们转
在我的代码中,当 List 为 null 时,我通常使用这种方法来避免 for 语句中的 NullPointerException: if (myList != null && myList.size
我正在尝试避免客户端出现 TIME_WAIT。我连接然后设置 O_NONBLOCK 和 SO_REUSEADDR。我调用 read 直到它返回 0。当 read 返回 0 时,errno 也为 0。我
在不支持异常的语言和/或库中,许多/几乎所有函数都会返回一个值,指示其操作成功或失败 - 最著名的例子可能是 UN*X 系统调用,例如 open( ) 或 chdir(),或一些 libc 函数。 无
我尝试按值提取行。 col1 df$col1[col1 == "A"] [1] "A" NA 当然我只想要“A”。如何避免 R 选择 NA 值?顺便说一句,我认为这种行为非常危险,因为很多人都会陷入
我想将两个向量合并到一个数据集中,并将其与函数 mutate 集成为 5 个新列到现有数据集中。这是我的示例代码: vector1% rowwise()%>% mutate(vector2|>
我是一名优秀的程序员,十分优秀!