结构体和类中使用的泛型

15
假设我们有以下使用泛型的struct定义:
public struct Foo<T>
{
    public T First; 
    public T Second;

    public Foo(T first)
    {
        this.First = first;
    }

}

编译器显示:

在控制权返回给调用者之前,'Foo.Second' 必须完全赋值

然而,如果 Foo 是一个类,则可以成功编译。

public class Foo<T>
{
    public T First; 
    public T Second;

    public Foo(T first)
    {
        this.First = first;
    }

}

为什么?为什么编译器对它们有不同的处理方式?此外,如果第一个Foo中没有定义构造函数,则可以编译通过。这种行为是为什么?

如果您使用结构体,应该考虑将其设置为不可变。请参见可变结构体是邪恶的。使其不可变的一种方法是声明public readonly T First; public readonly T Second;。然后在实例构造函数中自然地分配所有实例字段。另一方面,如果您真的不想分配所有字段,请链接(隐式和神奇的)无参数构造函数,例如:: this()(但我猜您已经知道了)。 - Jeppe Stig Nielsen
4个回答

22

1
值得注意的是,C#规则本质上是不可强制执行的,因为.net没有真正的“out”参数。当调用外部函数时,CLS规范中没有任何理由证明带有“Out()”属性的任何参数都会被编写,尽管C#可能会期望这样。如果使用不知道“Out()”属性的语言覆盖具有“Out”参数的虚拟方法,则该语言将把该参数视为“ref”参数,override方法可以根据需要编写或者不编写。值得注意的是,“var it = new StructType(params);”不会... - supercat
创建临时实例并将其复制到“it”中总是会执行这些操作。编译器可能只需直接改变现有的“it”实例的字段,而无需创建新的实例。它可以假设在读取之前所有字段都将被写入,但实际上并没有任何保证它们中的任何一个会被写入。 - supercat
6
为什么会这样?这简明地向楼主展示了他可以采取哪些措施来使得样本能够编译。 - driis

15

这是结构体通用的要求,与泛型无关。你的构造函数必须为所有字段分配一个值。

请注意,这里也会发生同样的错误:

struct Foo
{
    public int A;
    public int B;

    public Foo()
    {
        A = 1;
    }
}

2

因为在C#中,所有结构体的字段(内联或在构造函数中)都必须被分配。这是由于结构体的性质而决定的,与是否泛型无关。


0
其他答案正确解释了这种行为,但忽略了你问题的第二部分。为了完整起见,在此说明:

对于类,当您没有显式定义构造函数时,编译器将生成默认构造函数,该构造函数将默认值(例如对象的null,数字的0等)分配给每个字段。

对于结构体,结构体始终具有隐式的无参数构造函数,该构造函数分配默认值。


那不是正确的。在类中,所有字段都会被初始化为它们的默认值,无论你做什么。如果你将一个字段设置为 null,那么你就是在设置两次。 - Buh Buh
@BuhBuh 哦,你说得对。我把语言搞混了 :( - Jeff
1
顺便说一句:如果第二部分是在询问一个类,那么这个答案可能是正确的。但实际上,OP问的是为什么一个结构体(“第一个foo”)可以在没有任何构造函数定义的情况下是有效的。对于结构体来说,正确的答案是“结构体总是有一个隐式的无参构造函数”。在C#中,没有办法“禁止”new MyStruct()。它总是有效的。事实上,对于结构体,你甚至不能声明自己的无参构造函数来覆盖默认行为! - ToolmakerSteve
1
@ToolmakerSteve 谢谢!我已经更新了答案 - 如果你有时间,能否帮忙检查一下,确保我没有添加更多的错误信息 - 自从我不再是活跃的编码人员以来已经过去了几年! - Jeff

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