【问题标题】:Generics used in struct vs class结构与类中使用的泛型
【发布时间】:2012-12-05 20:27:45
【问题描述】:

假设我们有以下使用泛型的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 中没有定义构造函数,那么它会编译。为什么会有这种行为?

【问题讨论】:

  • 如果你使用结构体,你真的应该考虑让它不可变。见mutable structs are evil。使其不可变的一种方法是说public readonly T First; public readonly T Second;。然后在实例构造函数中分配所有实例字段是很自然的。另一方面,如果您真的不想分配给所有字段,请链接(隐式和神奇的)无参数构造函数,当然,例如:: this()(但我想您知道这一点)。

标签: c# class generics struct


【解决方案1】:

这是因为编译器规则强制必须在控制离开任何构造函数之前分配结构中的所有字段。

您可以通过以下方式使您的代码正常工作:

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

另见Why Must I Initialize All Fields in my C# struct with a Non-Default Constructor?

【讨论】:

  • 有趣的是,C# 规则本质上是不可执行的,因为 .net 没有真正的“out”参数。 C# 可能期望写入任何具有Out() 属性的参数,但在调用外部函数时,CLS 规范中没有任何内容证明任何此类假设的合理性。如果带有out 参数的虚方法在不知道Out() 属性的语言中被覆盖,则该语言会将参数视为ref 参数,覆盖方法可以写入或不写入它所看到的合身。值得注意的是var it = new StructType(params); 不...
  • ...总是通过创建一个临时实例然后复制到it来执行;编译器可能只是让构造函数直接改变现有it 实例的字段。它可能假设所有字段都会在被读取之前被写入,但实际上并不能保证它们中的任何一个都会被写入。
  • @BillW 为什么会这样?它简洁地向 OP 展示了他可以做些什么来编译他的样本。
【解决方案2】:

这是一般结构的要求——它与泛型无关。您的构造函数必须为所有字段赋值。

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

struct Foo
{
    public int A;
    public int B;

    public Foo()
    {
        A = 1;
    }
}

【讨论】:

    【解决方案3】:

    因为 C# 中的一条规则是,必须为结构(内联或构造函数)分配所有字段。这是因为结构性质。它与泛型或非泛型无关。

    【讨论】:

      【解决方案4】:

      其他答案正确解释了行为,但忽略了提及问题的第二部分,所以这里是为了完成。

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

      对于结构,结构 always 有一个隐式的无参数构造函数,它分配默认值。

      【讨论】:

      • 这不对。在类中,无论您做什么,所有字段都被初始化为其默认值。如果您将某个字段设置为 null,那么您将其设置两次。
      • @BuhBuh 哦,对了。把我的语言搞混了:(
      • 顺便说一句:这是第二部分的答案如果第二部分一直在询问一个 . OP 实际上询问为什么 struct (“第一个 foo”)在没有任何构造函数定义的情况下是有效的。结构的正确答案是“结构总是有一个隐式的无参数构造函数”。在 c# 中,没有办法“禁止”new MyStruct()。这总是有效的。事实上,对于一个结构体,你甚至不能声明自己的无参数构造函数来覆盖默认行为!
      • @ToolmakerSteve 谢谢!我已经更新了答案 - 如果你有时间,最好让你的眼睛睁开,以确保我没有加载任何错误信息 - 我已经好几年没有成为一名活跃的编码员了!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-02
      • 1970-01-01
      • 1970-01-01
      • 2016-09-14
      • 2013-03-20
      相关资源
      最近更新 更多