C# 结构体泛型构造函数

7
使用这段代码:
struct Foo<T1>
{
    public T1 Item1 { get; private set; }

    public Foo(T1 item1)
    {
        Item1 = item1;
    }
}

我遇到了这个错误:
自动实现属性 'Foo.Item1' 的后备字段在控制返回给调用者之前必须完全分配。请考虑从构造函数初始化程序中调用默认构造函数。
我的问题是,为什么在调用构造函数后属性 Item1 没有被完全分配?
编辑:将set更改为private set,因为这个问题与可变性无关。

10
请不要创建可变结构体。 - Mark Byers
1
@Lambert 不仅很容易创建出有问题的可变结构体,而且要正确使用它们也很困难。 - Tim Robinson
1
@Lambert:在C#中,可变结构体是一种“最糟糕的做法”。说“只要使用正确就没问题”是虚假的逻辑;问题在于你必须知道如何正确使用它们,而大多数人不知道。可变值类型会在你的代码中留下许多小炸弹,等待着一些毫无戒心的维护程序员通过对可变值类型进行某些看似微不足道的重构(将可变值类型视为引用类型)而意外引爆它们。 - Eric Lippert
1
@Lambert:是的,当重建不可变树结构时,通常需要重建脊柱,这是O(lg n)。对于一个有二万个项的树来说,这意味着15次分配。与您获得的好处相比,这几乎算不上什么,也就是说,当每个新的根指针从先前版本中回收了99.9%的内容时,您可以以极低的成本在撤消-重做缓冲区中拥有过去编辑的深度堆栈。 - Eric Lippert
2
@Lambert:我的观点是,我使用不可变的链接列表,因为它们在我需要插入的地方具有便宜的插入操作。如果我需要在列表中间插入,则不会使用不可变的链接列表,而是使用不可变的可连接双端队列——我在我的博客上实现了其中一种。当然,如果我正在使用一个明确期望通过突变来与之通信的C风格API,那么我会说这是一个糟糕的API,但我会按照设计来使用它。 - Eric Lippert
显示剩余15条评论
1个回答

17

在这里添加this()

public Foo(T1 item1) : this()
{
    Item1 = item1;
}

原因是你正在对属性进行赋值,编译器无法推断该属性将值分配给变量;在初始化实例之前,它可能进行其他操作,这是不允许的,因为结构体可能具有垃圾数据。因此,你必须首先使用默认构造函数对其进行初始化,然后再进行操作。


@Lambert:问题是,为什么编译器看不到显而易见的东西?在其他情况下,编译器对流程分析做得相当不错。 - Vlad
1
在C#中,对于值类型来说是不允许的,因为不能保证它们在某些情况下会被调用,比如创建数组时。 - user541686
3
@Vlad: 确实,编译器可以有一个规则,如果所调用的属性是自动属性,那么我们知道它除了设置字段外没有其他效果。然而,有两个问题。(1)那是一项工作,我们几乎没有完全在C# 3中使用自动属性;它们勉强适合时间表,(2)这意味着将自动属性更改为“普通”属性会给您带来这个奇怪的错误,您必须在构造函数中放置一个this()调用。我同意这有点笨拙,但至少它遵循了自动属性就像常规属性一样的原则。 - Eric Lippert
1
@Eric:与常规属性大小写一致是一个合理的原因(至少对我来说),谢谢。 - Vlad
1
@Danjen:没有固有的原因——这只是语言和运行时设计的结果。 - user541686
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接