访问 Form 上的成员可能会导致运行时异常,因为它是 marshal-by-reference 类的字段
我知道这个警告是什么并且知道如何解决它。
我的问题是为什么这会导致运行时错误?
访问 Form 上的成员可能会导致运行时异常,因为它是 marshal-by-reference 类的字段
我知道这个警告是什么并且知道如何解决它。
我的问题是为什么这会导致运行时错误?
您可能在谈论警告 CS1690,复制代码:
public class Remotable : MarshalByRefObject {
public int field;
}
public class Test {
public static void Run() {
var obj = new Remotable();
// Warning CS1690:
Console.WriteLine(obj.field.ToString());
}
}
在远程处理方案中,Test.Run 方法将使用 Remotable 对象的代理。为属性、方法或事件构建代理并不是什么大问题,只需创建一个包含替代项的 MethodTable。然而,字段是一个问题,没有什么可以“挂钩”的。对于 MBRO,JIT 编译器不再生成直接访问字段的代码,它会调用内置在 CLR 中的辅助方法,在这种情况下为 JIT_GetField32()。
该助手检查对象是否是代理,如果是这种情况,则使用远程管道获取远程值。或者如果不是直接访问该字段。但是,进行 ToString() 调用需要将值装箱。这是一个问题,装箱将值与代理隔离开来。无法确保装箱值始终是远程值的准确副本。每当 ToString() 方法使用该值来格式化字符串时,再次调用 JIT_GetField32() 是不可能的。
CS1690 的解决方法很简单,除了用属性包装字段之外,只需将字段值复制到局部变量中即可。现在很清楚,代码正在使用副本并且永远不会出现意外,因此编译器不必发出警告。
public static void Run() {
var obj = new Remotable();
var value = obj.field;
Console.WriteLine(value.ToString()); // No warning
}
除了@hans-passant 的建议之外,我认为解决此警告的另一种有用方法是将您的字段转换为属性。
public class Remotable : MarshalByRefObject {
public int field;
}
可能成为
public class Remotable : MarshalByRefObject {
public int field { get; set }
}
并且您不再收到任何警告!(Hans Passant 对此已经有了很好的解释,请参阅他的帖子)
显然,您不能总是更改您正在使用的对象(例如:为您生成字段的 WinForms),因此您可能不得不回退到使用临时变量。
或者你可以写:
var obj = new Remotable();
Console.WriteLine(((int) obj.field).ToString()); // No warning
在这里,您对演员表负责(拆箱)。
如果编组对象的另一端已经死亡,它将抛出一个运行时错误,指出引用的对象不再存在。