C#中结构变量的存储方式

How struct variables are stored in C#

我阅读了这个 Whosebug Question 我读了这个博客 here

完全看不懂。 这是我的疑惑。

 struct S
{
    private int x;
    private int y;

    public int X { get { return x; } }
    public int Y { get { return y; } }

    public S(int x, int y, bool panic)
    {          
        this.x = x;
        this.y = y;

        if (panic)
            throw new Exception();
    }
}

static class P
{
    static void Main()
    {
        S s = default(S);

        try
        {                

            s = new S(1, 2, false);

            Console.WriteLine("{0}, {1}", s.X, s.Y);

            s = new S(3, 4, true);
        }
        catch
        {
            Console.WriteLine("{0}, {1}", s.X, s.Y);
        }

        Console.ReadLine();
    }

}

在抛出异常之前,我在这里分配了值。但为什么它不分配给对象 s 我的意思是在我在 catch 块中编写的控制台中, 说 (1,2)。

因为在第二行又被初始化了,并且用(3,4)调用了构造函数。那么它是如何 (1,2).

我怎么看不懂。

还有,

Therefore, using the new operator on a value type allocates no additional memory. Rather, the memory already allocated for the value is used.

在那篇博客中,答案是否定的

如果是这样,是否正在使用新内存进行初始化。如果是这样, (1,2) 怎么会出现在 catch 块中。

由于我是 C# 的新手,所以我无法理解这一点。

我知道这是愚蠢的疑问,但请有人帮助我理解这个概念。

Because it is initialized again in the second line , and called the constructor with (3,4).

您调用了构造函数,但构造函数本身尚未完成 - 因此对 s 的赋值永远不会发生。

这个:

s = new S(3, 4, true);

相当于:

// Create the new value
S tmp = new S(3, 4, true);
// Copy it into the s variable
s = tmp;

第一个语句永远不会完成,因此不会发生赋值...因此您仍然会在 catch 块中看到 s 的第一个值。

转自博客Debunking another myth about value types:

The C# specification is clear on this point:

"If T is a struct type, an instance of T is created by allocating a temporary local variable"

That is, the statement

s = new S(123, 456);

actually means:

  • Determine the location referred to by s.
  • Allocate a temporary variable t of type S, initialized to its default value.
  • Run the constructor, passing a reference to t for "this".
  • Make a by-value copy of t to s.

您在第三阶段抛出异常:

  • Run the constructor, passing a reference to t for "this"

意思是最后一个阶段,复制到 s 永远不会发生,因此你看到 s 的当前值,在你的情况下是 1, 2.

在控制台中打印 s.X 和 s.Y。这是

的第一篇
s = new S(1, 2, false);

第二个赋值永远不会执行,因为抛出了异常。

在等号左侧的 S 赋值之前发生异常。

所以你的 catch 块写出了之前赋值的原始值。

大写S变量是Type,小s是实例。

结构构造函数作为方法实现,这些方法将正在构造的结构作为隐含的 ref 参数。在许多情况下,编译器会执行如下语句:

s = new StructType(4);

相当于

var temp = default(StructType);
StructType..ctor(out temp, 4);  // Not possible with C# syntax
s = temp;

然而,在某些情况下它不会这样做,而是简单地这样做:

StructType..ctor(out s, 4);

这可能是可以观察到的,尤其是在与其他语言编写的代码交互时,这些代码不支持 C# 用于标记 out 参数的属性。

例如,虽然不可能在 C# 中编写 IDictionary.TryGetValue 的实现,它不会将 default(TValue) 存储到其 out 参数,但该参数将被看到由其他语言作为 ref 参数;因此,用另一种语言编写的实现可能 return 而无需向其写入任何内容。如果结构构造函数将 this 传递给传入 IDictionaryTryGetValue 方法但不执行任何其他操作,则可以观察到结构构造的实际行为。