C# 为什么结构的字段在using语句之后被重置?
我注意到IDisposable结构有一些奇怪的行为。 dispose方法似乎是在字段设置为默认值的新实例上调用的 公共静态类示例 { 公共静态真空总管 { var数据=新的MyStruct; 使用数据 { data.Foo=某个字符串; Console.WriteLinedata.Foo;//一些字符串 } Console.WriteLinedata.Foo;//一些字符串 } } 公共结构MyStruct:IDisposable { 公共字符串Foo; 公共空间处置 { Console.writelinefo;//null! Foo=一些字符串; } } 我假设发生这种情况是因为对象在finally块中被强制转换为IDisposable,并且因为这里有一个值类型,所以创建了一个新实例。 我不明白的是为什么字段没有复制到新实例? 在框选结构时,将复制字段: var s=新的MyStruct; s、 Foo=1; var s2=MyStructobjects; Console.WriteLines.Foo//1. Console.WriteLines2.Foo//1.C# 为什么结构的字段在using语句之后被重置?,c#,struct,idisposable,C#,Struct,Idisposable,我注意到IDisposable结构有一些奇怪的行为。 dispose方法似乎是在字段设置为默认值的新实例上调用的 公共静态类示例 { 公共静态真空总管 { var数据=新的MyStruct; 使用数据 { data.Foo=某个字符串; Console.WriteLinedata.Foo;//一些字符串 } Console.WriteLinedata.Foo;//一些字符串 } } 公共结构MyStruct:IDisposable { 公共字符串Foo; 公共空间处置 { Console.wri
对于值类型,变量数据将在using语句的开头复制到另一个未命名的临时文件中。根据规范,这个副本的行为就像被装箱到IDisposable和Dispose调用note一样,但是C编译器实际上并没有装箱这个值,在文章的末尾会有更多的介绍。这记录在以下文件中: 表单的using语句
using (ResourceType resource = expression) statement
using (expression) statement
对应于三种可能的扩展之一。如果ResourceType是不可为null的值类型,则扩展为
{
ResourceType resource = expression;
try {
statement;
}
finally {
((IDisposable)resource).Dispose();
}
}
注意,using语句不是一个声明,只是一个表达式。本规范还包括以下内容:
表单的using语句
using (ResourceType resource = expression) statement
using (expression) statement
具有相同的三种可能的扩展。在本例中,ResourceType隐式表示表达式的编译时类型(如果有)。否则,接口IDisposable本身将用作ResourceType。资源变量在嵌入语句中不可访问,并且对嵌入语句不可见
因此,您对数据的修改不会在Dispose中看到,因为已经创建了副本。VS2019附带的C编译器的相对较新版本将针对这种情况发出警告
值是否已装箱
不。尽管在规范中出现了cast,甚至对C进行了一些反编译,但编译器是允许的,而且事实上不会对值进行装箱。Eric Lippert在评论中的链接也包含了一些关于这方面的补充细节。为了了解实际情况,让我们看看最后一部分中的IL:
IL_0023: ldloca.s 1
IL_0025: constrained. MyStruct
IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0030: endfinally
首先,将未命名的临时文件加载回计算堆栈。这是前面提到的未修改副本。下一步,魔法将通过网络发生。这是一条特殊指令,通知JIT正在直接对该类型进行调用,如果该类型是实现该方法的值类型,则不需要通过接口进行虚拟调用
Eric的文章提到了对C规范的更新,澄清了对装箱的省略,这可能就是这一点:
允许实现以不同方式实现给定的using语句,例如出于性能原因,只要行为与上述扩展一致
对于值类型,变量数据将在using语句的开头复制到另一个未命名的临时文件中。根据规范,这个副本的行为就像被装箱到IDisposable和Dispose调用note一样,但是C编译器实际上并没有装箱这个值,在文章的末尾会有更多的介绍。这记录在以下文件中: 表单的using语句
using (ResourceType resource = expression) statement
using (expression) statement
对应于三种可能的扩展之一。如果ResourceType是不可为null的值类型,则扩展为
{
ResourceType resource = expression;
try {
statement;
}
finally {
((IDisposable)resource).Dispose();
}
}
注意,using语句不是一个声明,只是一个表达式。本规范还包括以下内容:
表单的using语句
using (ResourceType resource = expression) statement
using (expression) statement
具有相同的三种可能的扩展。在本例中,ResourceType隐式表示表达式的编译时类型(如果有)。否则,接口IDisposable本身将用作ResourceType。资源变量在嵌入语句中不可访问,并且对嵌入语句不可见
因此,您对数据的修改不会在Dispose中看到,因为已经创建了副本。VS2019附带的C编译器的相对较新版本将针对这种情况发出警告
值是否已装箱
不。尽管在规范中出现了cast,甚至对C进行了一些反编译,但编译器是允许的,而且事实上不会对值进行装箱。Eric Lippert在评论中的链接也包含了一些关于这方面的补充细节。为了了解实际情况,让我们看看最后一部分中的IL:
IL_0023: ldloca.s 1
IL_0025: constrained. MyStruct
IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0030: endfinally
首先,将未命名的临时文件加载回计算堆栈。这是前面提到的未修改副本。下一步,魔法将通过网络发生。T
his是一条特殊指令,通知JIT正在直接对该类型进行调用,如果该类型是实现该方法的值类型,则不需要通过接口进行虚拟调用
Eric的文章提到了对C规范的更新,澄清了对装箱的省略,这可能就是这一点:
允许实现以不同方式实现给定的using语句,例如出于性能原因,只要行为与上述扩展一致
因为使用创建数据的副本。您使用的是什么版本的VS/C?2019年,我收到一条CS0728警告,其中明确指出了问题……请参阅,并给出了合理的结论,即您应该使用任何合理的现代版本的语言。如果您没有使用该语言的现代版本,则可能值得添加一个特定于语言的标记。任何时候,如果您有一个未声明为只读结构的结构,您可能处于危险的境地。readonly struct可以使您避免出现这样的错误,因为使用它可以创建数据的副本。您使用的是什么版本的VS/C?2019年,我收到一条CS0728警告,其中明确指出了问题……请参阅,并给出了合理的结论,即您应该使用任何合理的现代版本的语言。如果您没有使用该语言的现代版本,则可能值得添加一个特定于语言的标记。任何时候,如果您有一个未声明为只读结构的结构,您可能处于危险的境地。readonly struct可以使您避免出现这样的错误。