【问题标题】:Automatic Properties and Structures Don't Mix?自动属性和结构不混合?
【发布时间】:2017-08-18 05:37:33
【问题描述】:

在回复this post时,我在一些小结构中踢来踢去,意外地遇到了以下内容:

以下结构,使用 int 字段是完全合法的:

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Legal assignment.
    } 

    public int Size; 
}

但是,使用自动属性的以下结构无法编译:

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Compile-Time Error!
    } 

    public int Size{get; set;}
}

返回的错误是“'this' 对象在其所有字段都分配给之前不能使用”。我知道这是结构的标准过程:必须从结构的构造函数中直接(而不是通过属性的 set 访问器)分配任何属性的支持字段。

一种解决方案是使用显式支持字段:

struct MyStruct
{ 
    public MyStruct(int size)
    {
        _size = size;
    }

    private int _size;

    public int Size
    {
        get { return _size; }
        set { _size = value; }
    }
}

(请注意,VB.NET 不会有这个问题,因为在 VB.NET 中,所有字段在首次创建时都会自动初始化为 0/null/false。)

在 C# 中使用带有结构的自动属性时,这似乎是一个不幸的限制。从概念上考虑,我想知道这是否不是一个合理的地方,允许在结构的构造函数中调用属性集访问器的异常,至少对于自动属性?

这是一个小问题,几乎是边缘情况,但我想知道其他人对此有何看法......

【问题讨论】:

  • C# 中的字段也初始化为 0/null/false。请记住执行此操作的运行时,而不是特定的语言。 ;)
  • 不适用于 C# 中的结构字段。对于结构,字段必须由显式构造函数初始化,如果使用隐式无参数构造函数,则由调用者初始化。 VB.NET 没有这个限制,因此,上面的示例在 C# 中无法编译,但在 VB.NET 中也可以正常编译和运行。

标签: c# c#-3.0 struct automatic-properties


【解决方案1】:

从 C# 6 开始:这不再是问题


Becore C# 6,您需要调用默认构造函数才能使其工作:

public MyStruct(int size) : this()
{
    Size = size;
}

这里更大的问题是你有一个可变结构。这绝不是个好主意。我会成功的:

public int Size { get; private set; }

技术上不是不可变的,但足够接近。

使用最新版本的 C#,您可以对此进行改进:

public int Size { get; }

现在只能在构造函数中赋值。

【讨论】:

  • 我觉得说 never 有点强。一个人应该默认为不可变,但让它听起来像这样的命令“绝不是一个好主意”。 ;)
  • 使用绝对值从不是个好主意;-p(看看我在那里做了什么...)。实际上,如果您了解其中的含义,可变结构是可用的。问题是大多数人不理解它们,并且在学习过程中会花费大量时间和挫败感。
  • @MarcGravell,为什么可变结构不好?不应该像对待任何其他 POD 一样对待我的结构(例如:整数、双精度、浮点数)?
  • @dotnetN00b 是的,你应该这样做!提示:整数、双精度和浮点数都是不可变的:p 重新分配整个值没有问题(除了可能是原子的,它在线程场景中);问题是当您在原位编辑单个值的部分时,这通常会导致编辑丢失,除非您非常小心。
  • @supercat 为struct 成为ref SomeType 带来this 的乐趣....但我再次理解问题:我只是不同意这意味着SomeType 是“可变的”。相反,memory 是可变的 - 但内存始终是可变的。
【解决方案2】:

您可以通过首先调用默认构造函数来解决此问题:

struct MyStruct 
{
    public MyStruct(int size) : this() 
    {
        this.Size = size; // <-- now works
    }

     public int Size { get; set; }
}

【讨论】:

    【解决方案3】:

    另一个不为人知的解决方法是在Managed Extensibility Framework 的临时Tuple 类中发现一个(通过Krzysztof Koźmic):

    public struct TempTuple<TFirst, TSecond>
    {
        public TempTuple(TFirst first, TSecond second)
        {
            this = new TempTuple<TFirst, TSecond>(); // Kung fu!
            this.First = first;
            this.Second = second;
        }
    
        public TFirst First { get; private set; }
        public TSecond Second { get; private set; }
    

    (来自 Codeplex 的完整源代码:Tuple.cs

    我还注意到CS0188 的文档已更新为添加:

    如果您在尝试 初始化结构中的属性 构造函数,解决方法是改变 要指定的构造函数参数 支持字段而不是 财产本身。 自动实现 属性应避免在 结构,因为它们没有支持 字段,因此不能 以任何方式从 构造函数。

    所以我认为这意味着官方指导是在您遇到此问题时在您的结构中使用旧式属性,这可能比探索的其他两个替代方案中的任何一个都不那么晦涩(并且更易读)远。

    【讨论】:

    • 感谢更新错误报告。实际上,结构最有效的情况是使用显式支持字段。如果要初始化为 0/null/false 以外的值,则如果没有支持字段,则需要 两个 步骤:将 null 输出,然后设置实际值。
    • “功夫”系列真的很酷,我必须说。 (不能用一个类来完成。)但这与将构造函数声明为“public TempTuple(TFirst first, TSecond second):this()”是一样的,我认为这是一种更简洁的方法. (我们仍然会初始化每个字段两次。)
    猜你喜欢
    • 1970-01-01
    • 2011-06-19
    • 1970-01-01
    • 2014-08-25
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多