RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 615736
Accepted
nikola
nikola
Asked:2020-01-17 17:22:16 +0000 UTC2020-01-17 17:22:16 +0000 UTC 2020-01-17 17:22:16 +0000 UTC

没有静态构造函数可以吗?

  • 772

我很清楚静态构造函数是用来给静态变量赋值的,它在创建类的对象时首先被调用。

但是,如果我在声明变量时可以分配这些值,为什么还需要呢?

c#
  • 6 6 个回答
  • 10 Views

6 个回答

  • Voted
  1. Best Answer
    Sergey Teplyakov
    2020-01-18T13:53:38Z2020-01-18T13:53:38Z

    在 C# 中有几种初始化字段的方法:

    1. 在广告的地方

    2. 在构造函数中

    这是一个天真的例子:

    class Foo 
    {
      private string s1 = "s1";
      private string s2;
    
      private static string s3 = "s3";
      private static string s4;
    
      public Foo()
      {
        s2 = "s2";
      }
    
      static Foo()
      {
        s4 = "s4";
      }
    }
    

    从鸟瞰图来看,就地初始化相当简单:用于初始化字段的表达式被传递给适当的构造函数——一个用于实例字段的实例构造函数和一个用于静态字段的静态构造函数。

    这里有必要说一下为什么我们有两个构造函数。实例构造函数是某种辅助函数,旨在初始化正在创建的对象的实例。例如,我们可以说任何有效的对象都必须具有某些行为(不变量),而构造函数是必须提供它的特殊函数。任何东西都可以是不变的:从某些字段不为零的事实开始,以更复杂的规则结束,例如,字段дебет和кредит等于 0。

    除了对象不变量之外,还有类型不变量:即 有些条件不是针对特定对象,而是针对整个类型。类型的行为是使用静态成员来表达的,这意味着类型的“不变量”是静态变量的有效状态,其有效性的责任由静态构造函数提供。

    与对象构造函数不同,用户不调用类型构造函数。相反,它在第一次访问该类型时由 CLR 调用(我们将跳过确切的规则)。

    运行时行为存在细微差别,可以区分在声明站点使用字段初始值设定项与在构造函数中初始化这些相同的字段。静态和实例字段初始化器是语法糖,但重要的是要知道是哪一个!

    实例字段和构造函数的区别

    C# 编译器将所有实例字段初始值设定项移至实例构造函数。但这里的主要问题是究竟在哪里。

    通常,实例构造函数如下所示:

    // ctor
    // Код инициализаторов полей
    // Вызов конструктора базового класса
    // Код текущего конструктора
    

    这个算法有两个重要的结果。首先,实例字段初始化代码不只是放在构造函数中,而是放在构造函数的最开始,在调用基类构造函数之前,其次,它会被复制到所有实例构造函数中。

    第一句话非常重要(是的,这可以在面试时问到,在实际应用中可能会有用)。例如,如果有人决定在基类构造函数中调用虚方法,那么有些字段会被初始化,有些则不会。很容易猜到在声明处初始化的字段已经有效,其他字段将包含默认值。

    是的,是的,是的,在基类构造函数中调用虚方法是不好的,但实际上它发生了,你需要了解在这种情况下运行时会发生什么。

    静态字段和构造函数的区别

    使用静态构造函数,事情会更复杂,同时也更容易。在继承方面,静态字段的初始化方式与调用基类的静态构造函数无关。没门。通常,调用静态构造函数的过程与调用实例构造函数的过程不同。比如创建继承人的实例时,先调用继承人的静态构造函数,再调用基类的静态构造函数。而如果继承人的静态方法在抽动,那么根本不会自动调用基类的静态构造函数(只有当继承人的静态方法以某种方式拉动基类时才会调用)。

    但是困难在于什么时候调用静态构造函数。

    正如@Qwertiy 已经写过的,类中是否存在静态构造函数会影响何时调用此构造函数。静态构造函数的存在导致生成一个奇怪的特殊标志,然后它将告诉 CLR 您可以更自由地调用静态构造函数的时间,现在可以不在调用之前执行此操作第一次调用,但是,例如,在调用发生此调用的方法之前。

    这可能会影响应用程序的效率,因为现在假设第一次调用是在从 0 到 1000 的循环中,检查将进行一次,而不是数千次。

    结论

    字段初始化器(静态的和非静态的)是糖,但如果你不明白它的过度使用会导致什么,它可能会很苦。

    我通常使用以下经验法则。例如字段:您需要使用构造函数参数初始化字段 - (无选项)使用构造函数;否则,一个字段启动器。对于静态字段:在绝大多数情况下,我使用初始化程序。如果有很多代码,那么我会突出显示该方法。

    如果我需要使用静态构造函数来设置初始化顺序或更改类型初始化的语义,那么我会添加一个巨大的注释,说明为什么需要非常小心地处理这段代码。

    如果我需要为实例字段使用初始化程序以便在调用基类构造函数之前进行初始化,那么我将重构代码以便不需要这样做。例如,我突出了一个工厂方法。如果确实需要这种行为,那么这里需要一个两页的评论,解释为什么需要它以及为什么其他选项不合适。

    • 11
  2. Qwertiy
    2020-01-17T17:56:39Z2020-01-17T17:56:39Z

    调用静态构造函数有两个保证:

    • 基本类型初始化保证:类型的构造函数将在实例化之前或首次访问静态成员之前被调用。
    • “轻松”类型初始化保证 (BeforeFieldInit):类型构造函数将在第一次访问静态字段时调用(适用于 .NET 4.0+),并且可以在第一次类型访问之前随时调用。

    为了让 CLR 使用“宽松”模型,类型必须标有 BeforeFieldInit 标志。对于 C# 语言,选择是由显式静态构造函数的存在与否决定的:如果有显式静态构造函数,则使用基本类型初始化模型,如果没有静态构造函数,则使用宽松模型。

    public sealed class Singleton
    {
        private static readonly Singleton instance = new Singleton();
    
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Singleton()
        { }
    
        private Singleton()
        { }
    
        public static Singleton Instance { get { return instance; } }
    }
    

    实践表明,在大多数情况下,在没有显式构造函数的情况下,JIT 编译器会在调用使用该变量的方法之前立即调用静态变量初始化程序。

    如果您使用 Singleton 类的静态构造函数,那么行为将正是大多数开发人员所期望的 - 字段初始值设定项将在第一次调用之前不会被调用。

    来源: http:
    //sergeyteplyakov.blogspot.ru/2013/07/blog-post.html
    http://sergeyteplyakov.blogspot.ru/2011/08/blog-post.html
    http://sergeyteplyakov.blogspot.ru/ 2013/06/blog-post_28.html

    • 9
  3. αλεχολυτ
    2020-01-17T17:27:28Z2020-01-17T17:27:28Z

    静态构造函数的本质是它仅在创建指定类型的第一个元素之前被调用一次。当然,您可以在每次创建实例时都进行初始化并进行额外的检查,但是为什么呢?

    但是,如果我在声明变量时可以分配这些值,为什么还需要呢?

    如果静态字段的初始化顺序很重要,或者执行一些在使用静态成员的类型的简单初始化时不会发生的额外工作,它可能很有用。

    例如:

    using System;
    
    class A {
        public A() { Console.WriteLine("A"); }
    }
    
    class B {
        public B() { Console.WriteLine("B"); }
    }
    
    public class Test
    {
        static readonly A a;
        static readonly B b;
    
        public static void Main()
        {
        }
    
        static Test()
        {
            b = new B();
            Console.WriteLine("-");
            a = new A();        
        }
    
    }
    

    那。我们已经更改了静态字段的创建顺序,并添加了一个额外的步骤来执行简单的就地初始化无法实现的操作。

    • 6
  4. Pavel Mayorov
    2020-01-17T17:42:40Z2020-01-17T17:42:40Z

    并非每个对象都可以在一行中初始化。比如我经常使用的一个类,XmlSerializerNamespaces在调用构造函数后,需要多次调用Add方法。

    或者,如果您需要通过 Linq 表达式构建委托,则必须单独创建参数,尽管表达式的其余部分通常可以写在一行中。

    有时需要更复杂的初始化逻辑。


    此外,您需要了解在 C# 中,静态字段初始值设定项不同于静态构造函数。在编译过程中,它们都是简单地添加到它的开头。

    • 6
  5. Evgeny
    2020-01-18T15:06:23Z2020-01-18T15:06:23Z

    如果我们把一个静态类看作一个模板,那么这是一个“自由”的单例,类的所有静态成员都是这个单例的成员。同时,这个静态类在向它传递一个非静态类的实例时,将拥有对这个非静态类的字段的完全访问权限。

    静态类也称为 TYPE 类(静态构造函数分别称为类型构造函数),这意味着即使该类被声明为非静态的,实际上也会为其创建一个静态组件。

    好吧,那么我们必须从方便和必要出发:

    静态类的“实例”: 1. 无继承 2. 无处不在,无需传递对函数代码的任何引用(根据范围)

    非静态类的实例: 1. 有继承 2. 可以有多个实例 3. 只能通过传递给函数的引用访问

    • 2
  6. user2455111
    2020-01-17T17:47:43Z2020-01-17T17:47:43Z

    例如你有一个类

    internal class SomeType {
    private static Int32 s_x = 5;
    }
    

    此代码将等同于代码

    internal class SomeType {
    private static Int32 s_x;
    static SomeType() { s_x = 5; }
    }
    

    如果你使用第一个选项,那么编译器会自动生成一个类型构造函数

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5