我读了这个StackOverflow 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();
}
}
在抛出异常之前我分配了值。但是为什么不分配给对象我的意思是在我在catch块中编写的控制台中, 说(1,2)。
因为它在第二行中再次初始化,并用(3,4)调用构造函数。怎么样(1,2)。
不知怎的,我无法理解这一点。
而且,
因此,在值类型上使用new运算符不会分配额外的内存。而是使用已为该值分配的内存。
在那篇博客中,答案是否定的。
如果是这种情况,是否使用新内存进行初始化。如果是这样,那么(1,2)是如何进入捕获区的。
由于我是C#的新手,我无法理解这一点。
我知道它的愚蠢怀疑,但有人请帮助我理解这个概念。
答案 0 :(得分:4)
引自博客Debunking another myth about value types:
C#规范在这一点上很清楚:
"如果T是结构类型,则通过分配临时局部变量来创建T的实例"
即声明
s = new S(123, 456);
实际上意味着:
- 确定s。
引用的位置- 分配S类型的临时变量t,初始化为其默认值。
- 运行构造函数,将对t的引用传递给"此"。
- 制作t到s的副本值副本。
您在第三阶段抛出异常:
- 运行构造函数,将对t的引用传递给"此"
意味着最后一个阶段,副本到s
永远不会发生,因此您会看到s
的当前值,在您的情况下为1, 2
。
答案 1 :(得分:2)
因为它在第二行再次初始化,并用(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;
第一个语句永远不会完成,因此分配不会发生...所以你仍然会在s
块中看到catch
的第一个值。
答案 2 :(得分:0)
在控制台中打印s.X和s.Y.这是
中的第一个s = new S(1, 2, false);
永远不会执行第二个赋值,因为抛出了异常。
答案 3 :(得分:0)
在分配给等号左侧的S之前发生异常。
所以你的catch块会写出前一个赋值的原始值。
Capital S变量是Type,小s是实例。
答案 4 :(得分:0)
结构构造函数实现为将结构构造为隐含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
参数;因此,用另一种语言编写的实现可能会返回而不会向其写入任何内容。如果结构构造函数将this
传递给传入的TryGetValue
的{{1}}方法,但不执行任何其他操作,则可以观察到结构构造的实际行为。