C# C禁止调用默认构造函数的根本原因是什么?

C# C禁止调用默认构造函数的根本原因是什么?,c#,C#,这是我在这里提出的另一个问题的后续问题: 前一个是关于如何设计的,现在的问题是为什么微软会这样设计 更新:我的问题是: 为什么我不能在另一个构造函数的末尾直接调用构造函数,而我可以在开头调用呢 如果他们在最后禁止调用,为什么不在开始时也禁止直接调用呢?我猜,但可能很简单:您很少需要它,而当您这样做时,通常会有更好的选项—私有/受保护的初始化方法,可以充分完成这项工作。而且没有忘记初始化它的风险,或者调用依赖于尚未初始化的数据的方法,这就是为什么在ctor中调用虚拟方法有点风险的原因 唯一需要注

这是我在这里提出的另一个问题的后续问题:

前一个是关于如何设计的,现在的问题是为什么微软会这样设计

更新:我的问题是:

为什么我不能在另一个构造函数的末尾直接调用构造函数,而我可以在开头调用呢


如果他们在最后禁止调用,为什么不在开始时也禁止直接调用呢?

我猜,但可能很简单:您很少需要它,而当您这样做时,通常会有更好的选项—私有/受保护的初始化方法,可以充分完成这项工作。而且没有忘记初始化它的风险,或者调用依赖于尚未初始化的数据的方法,这就是为什么在ctor中调用虚拟方法有点风险的原因


唯一需要注意的是,您不能将只读字段与post-ctor初始化方法一起使用,但这并不像让一个角落的情况变得更复杂那样糟糕。

我猜,但可能很简单:您很少需要它,当您这样做时,通常会有更好的选项—私有/受保护的初始化方法—来充分完成这项工作。而且没有忘记初始化它的风险,或者调用依赖于尚未初始化的数据的方法,这就是为什么在ctor中调用虚拟方法有点风险的原因


唯一需要注意的是,您不能将只读字段与post-ctor初始化方法一起使用,但这比使角落情况变得更复杂要简单。

构造函数的目的是初始化对象。 当您想要将一些初始参数传递给对象时,会使用重载构造函数

因此,设计的目的是让一个巨型构造函数接受所有变量并进行所有初始化,让其他构造函数传播变量,并将默认值设置为用户未传递的参数


其思想是让一个方法进行初始化,而不是让单独的构造函数分别执行其他操作。

构造函数的目的是初始化对象。 当您想要将一些初始参数传递给对象时,会使用重载构造函数

因此,设计的目的是让一个巨型构造函数接受所有变量并进行所有初始化,让其他构造函数传播变量,并将默认值设置为用户未传递的参数


其思想是让一个方法进行初始化,而不是让单独的构造函数分别执行其他操作。

只有在您不指定自己的构造函数时,编译器才会创建默认构造函数。这是一种语法上的糖分,使快速创建类变得容易

通过编写自己的构造函数,实际上就是在声明是,您对对象的初始化方式感兴趣。未生成默认构造函数


由于禁用默认构造函数只能通过编写另一个构造函数来实现,因此允许用户调用默认构造函数将无法禁用它。

默认构造函数仅在您未指定自己的构造函数时由编译器创建。这是一种语法上的糖分,使快速创建类变得容易

通过编写自己的构造函数,实际上就是在声明是,您对对象的初始化方式感兴趣。未生成默认构造函数


由于禁用默认构造函数,只需编写另一个构造函数,允许用户调用默认构造函数就不可能禁用它。 p> irc从C++设计中得到,必须以自上而下的顺序调用构造函数,因为基类实例在其构造函数完成执行之前不能保证可用。除非事先知道基本对象已完全初始化并准备就绪,否则无法安全地构造派生对象。如果这似乎是微不足道的一点,那么考虑基类包含只在构造函数中正确初始化的私有状态的情况。AFAIK在CLR中的推理是相同的


同样的推理适用于相反的方向。在派生类实例完成之后,只对基类实例进行销毁/定稿是安全的,因为派生对象可能依赖于基对象的状态仍然有效。C++中的

< P.Irc,必须以自上而下的顺序调用构造函数,因为基类实例在其构造函数完成执行之前不能保证可用。除非事先知道基本对象已完全初始化并准备就绪,否则无法安全地构造派生对象。如果这似乎是微不足道的一点,那么考虑基类包含只有初始状态的私有状态的情况。 在它的构造器中进行适当的调整。AFAIK在CLR中的推理是相同的


同样的推理适用于相反的方向。只有在派生类实例完成后,才可以安全地销毁/最终确定基类实例,因为派生对象可能依赖于基类对象的状态仍然有效。

我认为您描述的行为的技术原因在C规范中有所暗示:

任何变量初始值设定项都是作为构造函数代码的一部分调用的,如果您能够直接调用构造函数(从编译器的角度来看,这是一个非常特殊的函数),您可能会引发其他行为。使用构造函数链接方法,在构造函数定义上使用:this,编译器尊重您安全调用另一个构造函数的尝试


我认为,除了在同一类继承中调用另一个构造函数之外,没有什么真正的要求——如果不定义另一个函数,就没有什么不能做的,老实说,我更喜欢这样做。如果我的构造函数太长,我通常会觉得把它放到单独的函数中更舒服,因为我经常需要在构造函数之外调用复杂的重置或初始化函数。

我认为您描述的行为的技术原因在这里的C规范中有所暗示:

任何变量初始值设定项都是作为构造函数代码的一部分调用的,如果您能够直接调用构造函数(从编译器的角度来看,这是一个非常特殊的函数),您可能会引发其他行为。使用构造函数链接方法,在构造函数定义上使用:this,编译器尊重您安全调用另一个构造函数的尝试


我认为,除了在同一类继承中调用另一个构造函数之外,没有什么真正的要求——如果不定义另一个函数,就没有什么不能做的,老实说,我更喜欢这样做。如果我的构造函数变得冗长,我通常会觉得把它放到单独的函数中会更舒服,因为我经常需要在构造函数之外调用复杂的重置或初始化函数。

Eric Lippert对创建对象时初始值设定项和构造函数的运行顺序有一个大致的了解。他在书中说:派生度越高的构造函数可能依赖于派生度越低的构造函数初始化的状态,因此构造函数应该按从基到派生的顺序运行。

Eric Lippert对创建对象时初始化器和构造函数的运行顺序有一个大致的了解。他在书中说:派生度越高的构造函数可能依赖于派生度越低的构造函数初始化的状态,因此构造函数应该按照从基到派生的顺序运行

为什么我不能调用构造函数 直接在另一个结尾处 构造函数,而我可以在 开始

好吧,让我们把它分为两种情况。1您正在调用一个this构造函数,2您正在调用一个base构造函数

假设你是第一种情况。此场景的典型使用模式是有一组接受不同参数的ctor,然后全部馈送到一个主ctor(通常是私有的),该主ctor执行所有实际工作。通常,公共构造函数没有自己的主体,因此在空块之前或之后调用另一个构造函数没有区别

假设您在第一种情况下,在每个ctor中都在工作,并且您希望在当前ctor开始之外的时间调用其他ctor

在这种情况下,您可以通过将不同的actor所做的工作提取到方法中,然后按照您喜欢的顺序调用actor中的方法来轻松实现这一点。这比发明允许在任意位置调用其他构造函数的语法要好。有许多设计原则支持这一决策。其中两项是:

一有两种方法做同一件事会造成混乱;这增加了精神成本。在C语言中,我们通常有两种方法来做同一件事,但是在这些情况下,我们希望这两种不同的方法都是引人注目的、有趣的、功能强大的,并且有明确的优缺点。例如,查询理解与流畅查询是构建查询的两种截然不同的方式。像调用任何其他方法一样调用一个ctor,似乎有两种方法可以做某事——调用一个初始化方法——但没有令人信服或有趣的回报

2我们必须添加新的语言语法才能做到这一点。新的语法代价很高;它必须经过设计、实施、测试和记录——这是我们的成本。但这对您来说代价更高,因为您必须了解语法的含义,否则您无法阅读或维护其他人的代码。这是另一个成本;再一次,如果我们觉得语法对我们有明显的、令人信服的、巨大的好处,那么我们只需要付出巨大的代价来添加语法 我们的客户。我看不出这有什么好处

简而言之,无需添加功能即可轻松实现所需的构造控制流,添加功能也不会带来显著的好处。语言中没有新的有趣的表现力

对于基本场景,这非常简单。您永远不希望在派生构造函数之后调用基构造函数。这是正常依赖规则的反转。派生代码应该能够依赖于设置了对象基本状态的基本构造函数;基本构造函数永远不应该依赖于已设置派生状态的派生构造函数

为什么我不能调用构造函数 直接在另一个结尾处 构造函数,而我可以在 开始

好吧,让我们把它分为两种情况。1您正在调用一个this构造函数,2您正在调用一个base构造函数

假设你是第一种情况。此场景的典型使用模式是有一组接受不同参数的ctor,然后全部馈送到一个主ctor(通常是私有的),该主ctor执行所有实际工作。通常,公共构造函数没有自己的主体,因此在空块之前或之后调用另一个构造函数没有区别

假设您在第一种情况下,在每个ctor中都在工作,并且您希望在当前ctor开始之外的时间调用其他ctor

在这种情况下,您可以通过将不同的actor所做的工作提取到方法中,然后按照您喜欢的顺序调用actor中的方法来轻松实现这一点。这比发明允许在任意位置调用其他构造函数的语法要好。有许多设计原则支持这一决策。其中两项是:

一有两种方法做同一件事会造成混乱;这增加了精神成本。在C语言中,我们通常有两种方法来做同一件事,但是在这些情况下,我们希望这两种不同的方法都是引人注目的、有趣的、功能强大的,并且有明确的优缺点。例如,查询理解与流畅查询是构建查询的两种截然不同的方式。像调用任何其他方法一样调用一个ctor,似乎有两种方法可以做某事——调用一个初始化方法——但没有令人信服或有趣的回报

2我们必须添加新的语言语法才能做到这一点。新的语法代价很高;它必须经过设计、实施、测试和记录——这是我们的成本。但这对您来说代价更高,因为您必须了解语法的含义,否则您无法阅读或维护其他人的代码。这是另一个成本;同样,如果我们觉得对我们的客户有明显的、引人注目的、巨大的好处,我们只需要花费巨大的费用来添加语法。我看不出这有什么好处

简而言之,无需添加功能即可轻松实现所需的构造控制流,添加功能也不会带来显著的好处。语言中没有新的有趣的表现力


对于基本场景,这非常简单。您永远不希望在派生构造函数之后调用基构造函数。这是正常依赖规则的反转。派生代码应该能够依赖于设置了对象基本状态的基本构造函数;基本构造函数永远不应该依赖于已设置派生状态的派生构造函数

给那些回答你上一篇文章的人一些街头信条,并把你之前的问题标记为已回答:你能问一个更具体的问题吗?为什么它是这样设计的?这是一个很难有意义地回答的问题,因为答案总是一样的:因为这似乎是最好的解决方法。设计总是一个妥协的过程,面对几十个相互竞争的原则,这些原则必须相互平衡。不会有一个简单明了的答案来回答为什么会这样设计?问题-一个正确的答案涉及到对语言设计哲学的深入而漫长的讨论,我不准备一大早就给出这些。@Jayden当然,我只需要重新阅读,选择我认为最好的答案:@Eric每个设计都是一种妥协,但它们是什么?说它是最好的是一个简单的答案,即使是一个糟糕的设计,你也可以给出。糟糕的设计存在,或者编程语言不会进化。我认为真正的问题是一个地方或者我想要的任何地方。如果你提供开始或结束,那么有人会问:为什么有人禁止我在我想要的地方给构造函数打电话?如果您选择一个定义的位置,它必须是一个开始,因为否则继承将是一场噩梦,您需要调用base。如果您想在任何地方执行此操作,编译器可能会

我要开始把构造函数当作一个常规函数来对待,这样会有一些折衷,powers-than-I可能会联系到Eric,我正在看你的博客。给那些回答你上一篇文章的人一些街头信条,并把你之前的问题标记为已回答:你能问一个更具体的问题吗?为什么它是这样设计的?这是一个很难有意义地回答的问题,因为答案总是一样的:因为这似乎是最好的解决方法。设计总是一个妥协的过程,面对几十个相互竞争的原则,这些原则必须相互平衡。不会有一个简单明了的答案来回答为什么会这样设计?问题-一个正确的答案涉及到对语言设计哲学的深入而漫长的讨论,我不准备一大早就给出这些。@Jayden当然,我只需要重新阅读,选择我认为最好的答案:@Eric每个设计都是一种妥协,但它们是什么?说它是最好的是一个简单的答案,即使是一个糟糕的设计,你也可以给出。糟糕的设计存在,或者编程语言不会进化。我认为真正的问题是一个地方或者我想要的任何地方。如果你提供开始或结束,那么有人会问:为什么有人禁止我在我想要的地方给构造函数打电话?如果您选择一个定义的位置,它必须是一个开始,因为否则继承将是一场噩梦,您需要调用base。如果你想在任何地方使用它,编译器可能必须开始把构造函数当作一个常规函数来处理,而且可能会有一些折衷,powers-than-I可能会说,Eric,我正在看你的博客。如果不定义另一个函数,你什么都做不了,过程编程也没有什么不能做的:事实上,当我问为什么我不能直接调用构造函数时,我忘记了细化,而是问:为什么我不能在另一个构造函数的末尾直接调用构造函数,而我可以在开头调用。如果不定义另一个函数,你什么都做不了,对于过程编程也没有什么不能做的:事实上,当我问为什么我不能直接调用构造函数时,我忘记了细化,而是问:为什么我不能在另一个构造函数的末尾直接调用一个构造函数,而我可以在一开始调用。这可能非常罕见,但即使是让项目偏离轨道的情况也非常罕见。你能证明当你需要的时候,有更好的选择吗。正如我在update中所说的,实际上可以直接调用构造函数,但只能在开始时调用,而不能在结束时调用。那么,这真的是因为他们没有费心去实现一个有用的稀有特性,还是因为这是一个糟糕的设计?这可能非常罕见,但即使是让一个项目偏离轨道的情况也非常罕见。你能证明当你需要的时候,有更好的选择吗。正如我在update中所说的,实际上可以直接调用构造函数,但只能在开始时调用,而不能在结束时调用。那么,这真的是因为他们没有费心去实现一个有用的稀有功能,还是因为这是一个糟糕的设计?请看我的更新,因为我的问题不够精确。请看我的更新,因为我的问题不够精确。我喜欢这种数学证明,我们必须重新阅读它,因为我的头脑是这样的本周我的工作忙得不可开交:另外,有了新的C4.0默认参数功能,我们会看到Eric第一次调用this构造函数的需求会减少。我喜欢这种数学证明,我必须重新阅读它,因为我的脑袋本周工作忙得不可开交:还有,有了新的C 4.0默认参数功能,我们将看到Eric第一次调用this构造函数的需求减少了。好的,谢谢你的全面回答,会让我摸不着头脑的:好的,谢谢你的全面回答,会让我摸不着头脑的: