当前位置:  开发笔记 > 编程语言 > 正文

为什么我不能在.NET中为结构定义默认构造函数?

如何解决《为什么我不能在.NET中为结构定义默认构造函数?》经验,为你挑选了5个好方法。

在.NET中,值类型(C#struct)不能包含没有参数的构造函数.根据这篇文章,这是CLI规范要求的.会发生什么,对于每个值类型,都会创建一个默认构造函数(由编译器?),它将所有成员初始化为零(或null).

为什么不允许定义这样的默认构造函数?

一个微不足道的用途是有理数:

public struct Rational {
    private long numerator;
    private long denominator;

    public Rational(long num, long denom)
    { /* Todo: Find GCD etc. */ }

    public Rational(long num)
    {
        numerator = num;
        denominator = 1;
    }

    public Rational() // This is not allowed
    {
        numerator = 0;
        denominator = 1;
    }
}

使用当前版本的C#,默认的Rational 0/0并不是那么酷.

PS:默认参数是否有助于解决C#4.0或者是否会调用CLR定义的默认构造函数?


Jon Skeet回答道:

要使用您的示例,当有人执行时您希望发生什么:

 Rational[] fractions = new Rational[1000];

它应该通过你的构造函数1000次?

当然应该,这就是我首先编写默认构造函数的原因.当没有定义显式默认构造函数时,CLR应该使用默认的归零构造函数; 这样你只需支付你使用的费用.然后,如果我想要一个1000个非默认Rational的容器(并希望优化1000个结构),我将使用一个List而不是一个数组.

在我看来,这个原因并不足以阻止默认构造函数的定义.



1> Jon Skeet..:

注意:下面的答案是在C#6之前很长一段时间编写的,它计划引入在结构中声明无参数构造函数的能力 - 但它们仍然不会在所有情况下被调用(例如,用于数组创建) (最后)此功能未添加到C#6).


编辑:由于格劳恩沃尔夫对CLR的洞察力,我编辑了下面的答案.

CLR允许值类型具有无参数构造函数,但C#不具有参数构造函数.我相信这是因为它会引入一种期望,即构造函数不会被调用.例如,考虑一下:

MyStruct[] foo = new MyStruct[1000];

CLR能够通过分配适当的内存并将其归零来非常有效地完成此任务.如果它必须运行MyStruct构造函数1000次,那将效率低得多.(事实上,它没有-如果你这样做有一个参数的构造函数,当你创建一个数组它不会运行,或当你有一个未初始化的实例变量.)

C#中的基本规则是"任何类型的默认值都不能依赖于任何初始化".现在他们可以允许定义无参数构造函数,但是不需要在所有情况下执行构造函数 - 但这会导致更多的混淆.(或至少,所以我相信这个论点.)

编辑:要使用您的示例,当有人执行时您希望发生什么:

Rational[] fractions = new Rational[1000];

它应该通过你的构造函数1000次?

如果没有,我们最终会得到1000个无效的理由

如果确实如此,那么如果我们要用真实值填充数组,那么我们可能会浪费大量的工作.

编辑:(回答更多问题)无参数构造函数不是由编译器创建的.就CLR而言,值类型不必具有构造函数 - 尽管事实证明它可以在IL中编写它.当您new Guid()在C#中写入" "时,如果您调用普通构造函数,则会发出不同的IL.有关该方面的更多信息,请参阅此SO问题.

怀疑框架中没有无参数构造函数的任何值类型.毫无疑问NDepend可以告诉我,如果我问得好吗...... C#禁止它的事实是一个足够大的提示让我认为这可能是一个坏主意.


@Joel:虽然这并没有真正解释这个特殊的限制,是吗?
更简短的解释:在C++中,struct和class只是同一枚硬币的两面.唯一真正的区别是一个是默认公开,另一个是私有.在.Net中,结构和类之间存在更大的差异,理解它很重要.
CLR允许值类型具有无参数构造函数.是的,它会为数组中的每个元素运行它.C#认为这是一个坏主意并且不允许它,但你可以编写一种.NET语言.
@ FifaEarthCup2014:你必须更加明确"浪费大量工作"的意思.但无论哪种方式,它都不会调用构造函数1000次.如果`Rational`是一个类,那么最终会得到一个包含1000个空引用的数组.
抱歉,我对以下内容感到困惑。如果“理性”是类而不是结构,那么“理性[]分数=新的Rational [1000];”是否还会浪费工作量?如果是这样,为什么类具有默认的ctor?

2> user42467..:

结构是值类型,值类型一旦声明就必须具有默认值.

MyClass m;
MyStruct m2;

如果你声明上面的两个字段而没有实例化,那么打破调试器,m将是null但m2不会.鉴于此,无参数构造函数没有任何意义,实际上结构上的所有构造函数都是赋值,事物本身只是通过声明它就已存在.实际上m2可以很高兴地用在上面的例子中并调用它的方法,如果有的话,它的字段和属性被操纵!


C++中的行为是,如果类型具有默认构造函数,则在没有显式构造函数的情况下创建此类对象时使用该构造函数.这可以在C#中用于使用默认构造函数初始化m2,这就是为什么这个答案没有帮助的原因.
@Tarik.我不同意.相反,无参数构造函数会非常有意义:如果我想创建一个"矩阵"结构,它总是有一个单位矩阵作为默认值,你怎么能用其他方法呢?
不知道为什么有人投票给你.你似乎是这里最正确的答案.
onester:如果你不希望结构在声明时调用自己的构造函数,那么就不要定义这样的默认构造函数!:)这是Motti的说法

3> Adiii..:

虽然CLR允许它,但C#不允许结构具有默认的无参数构造函数.原因是,对于值类型,编译器默认情况下既不生成默认构造函数,也不生成对默认构造函数的调用.因此,即使您碰巧定义了默认构造函数,也不会调用它,这只会让您感到困惑.

为避免此类问题,C#编译器不允许用户定义默认构造函数.并且因为它不生成默认构造函数,所以在定义字段时无法初始化字段.

或者最大的原因是结构是值类型,值类型由默认值初始化,构造函数用于初始化.

您不必使用new关键字实例化您的结构.它反过来像一个int; 你可以直接访问它.

结构不能包含显式无参数构造函数.Struct成员会自动初始化为其默认值.

结构的默认(无参数)构造函数可以设置与全零状态不同的值,这将是意外行为.因此,.NET运行时禁止结构的默认构造函数.



4> 小智..:

您可以创建一个初始化并返回默认"有理"数字的静态属性:

public static Rational One => new Rational(0, 1); 

并使用它像:

var rat = Rational.One;


在这种情况下,`Rational.Zero`可能会有点混乱.

5> Joel Coehoor..:

更简短的解释:

在C++中,struct和class只是同一枚硬币的两面.唯一真正的区别是,一个是默认公开,另一个是私人.

在.NET中,结构和类之间存在更大的差异.主要的是struct提供了值类型语义,而class提供了引用类型语义.当您开始考虑此更改的含义时,其他更改也会开始变得更有意义,包括您描述的构造函数行为.


你必须更明确一点,这是如何暗示值与参考类型分裂我没有得到它...
人们想知道为什么其他构造函数*被允许
推荐阅读
雨天是最美
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有