在. 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,这不是很酷。

在c# 4.0中,默认参数会帮助解决这个问题吗?还是会调用clr定义的默认构造函数?


琼恩·斯基特回答说:

用你的例子来说,如果有人这么做了,你希望发生什么: Rational[]分数= new Rational[1000]; 它应该遍历构造函数1000次吗?

当然应该,这就是为什么我首先写默认构造函数的原因。当没有明确定义默认构造函数时,CLR应该使用默认的归零构造函数;这样你只需要为你所使用的付费。然后,如果我想要一个包含1000个非默认Rational的容器(并且想要优化掉这1000个结构),我将使用List<Rational>而不是数组。

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


当前回答

你可以创建一个静态属性,初始化并返回一个默认的“有理数”:

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

像这样使用它:

var rat = Rational.One;

其他回答

从c# 10.0开始,你可以:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#parameterless-constructors-and-field-initializers

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

    public Rational(long num = 0, long denom = 1)   // This is allowed!!!
    {
        numerator   = num;
        denominator = denom;
    }
}

只是特殊情况。如果你看到分子是0,分母是0,就假装它有你真正想要的值。

注意:下面的答案是在c# 6之前很长时间写的,c# 6计划引入在结构中声明无参数构造函数的能力——但它们仍然不会在所有情况下被调用(例如用于数组创建)(最后这个特性没有添加到c# 6中)。


编辑:鉴于Grauenwolf对CLR的深刻见解,我编辑了下面的答案。

CLR允许值类型具有无参数的构造函数,但c#不允许。我相信这是因为它会引入一种期望,即在构造函数没有调用时调用它。例如,考虑这个:

MyStruct[] foo = new MyStruct[1000];

CLR能够通过分配适当的内存并将其全部归零来非常有效地做到这一点。如果它必须运行MyStruct构造函数1000次,那么效率将大大降低。(事实上,它不会——如果你有一个无参数的构造函数,当你创建一个数组,或当你有一个未初始化的实例变量时,它不会运行。)

c#的基本规则是“任何类型的默认值都不能依赖于任何初始化”。现在他们可以允许定义无参数的构造函数,但不要求在所有情况下都执行该构造函数——但这将导致更多的混乱。(至少我是这么认为的。)

编辑:用你的例子来说,如果有人这样做了,你希望发生什么:

Rational[] fractions = new Rational[1000];

它应该遍历构造函数1000次吗?

否则,我们就会得到1000个无效理性 如果是这样,那么如果我们要用实值填充数组,我们就可能浪费了大量的工作。

EDIT:(回答了更多的问题)无参数构造函数不是由编译器创建的。就CLR而言,值类型不必有构造函数——尽管如果你用IL写它,结果是可以的。当你用c#写“new Guid()”时,如果你调用一个普通的构造函数,它会发出不同的IL。关于这方面,请参阅这个SO问题。

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

你可以创建一个静态属性,初始化并返回一个默认的“有理数”:

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

像这样使用它:

var rat = Rational.One;