首页 > 解决方案 > C# 中的显式结构需要双重初始化值

问题描述

以下 C# 结构用于表示颜色分量和 32 位颜色值本身的联合。问题是编译器给出了错误:

错误 CS0171 在将控制权返回给调用者之前必须完全分配字段“Color.ARGB”

是否可以在不初始化数据两次的情况下摆脱此错误?这是 C# 的预期行为吗?如果我初始化两次,JIT 会检测到双重初始化并且只执行第二次吗?

[StructLayout(LayoutKind.Explicit)]
public struct Color
{
    public Color(byte r, byte g, byte b, byte a = 0xff)
    {
        ARGB = 0; // The init I shouldn't have to do
        A = a;
        R = r;
        G = g;
        B = b;
    }

    [FieldOffset(0)]
    public byte B;
    [FieldOffset(1)]
    public byte G;
    [FieldOffset(2)]
    public byte R;
    [FieldOffset(3)]
    public byte A;
    [FieldOffset(0)]
    public uint ARGB;

    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
}

标签: c#

解决方案


是否可以在不初始化数据两次的情况下摆脱此错误?

是和不是。

这里的假设是成员尚未“初始化两次”。当您从内存分配器(从堆或堆栈中)获取新结构时,它将自动归零。

正如 Naidu 的回答所指出的,调用默认构造函数向编译器表明“运行时必须将这个东西归零,如果它还没有;我想断言我可以接受没有被构造函数写入的对象的任何部分留在里面它的默认状态“。

在实践中,抖动通常已经初始化为零,因此通常不会进行额外的初始化。但是,内存分配器自动将状态初始化为零的行为取决于运行时实现。同样,如果它知道每个字段都已初始化,则抖动是否可以优化归零行为是依赖于实现的行为。

这里有一些微妙之处。例如,假设内存没有清零,因为抖动推断出您的构造函数写入了每个字段。现在假设在构造函数的中途抛出了一个线程中止异常。另一个线程是否有可能观察到对象的未归零、未写入状态?如果事实上有可能,那会发生什么地狱般的行为?考虑一下。

这是 C# 的预期行为吗?

是的。

编译器不知道您正在创建一个类型不安全的联合。它不知道那些属性的含义。

如果我初始化两次,JIT 会检测到双重初始化并且只执行第二次吗?

在许多不同的平台上存在许多不同的抖动。如果您想回答您的问题,请使用所有可能的配置对所有这些问题进行尝试,看看会发生什么

无论如何,你很可能担心没有什么重要的事情。将零写入内存非常快。进行不必要的零写入可能不是您程序的瓶颈。


推荐阅读