访问Form上的成员可能会导致运行时异常,因为它 是一个按参考编组的字段
我知道这个警告是什么,知道如何解决它。
我的问题是为什么会导致运行时错误?
答案 0 :(得分:25)
你可能正在谈论警告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
}
答案 1 :(得分:4)
除了来自@ hans-passant的建议之外,我认为修复此警告的另一个有用方法是将您的字段变为属性。
public class Remotable : MarshalByRefObject {
public int field;
}
可能会成为
public class Remotable : MarshalByRefObject {
public int field { get; set }
}
你不再收到任何警告! (Hans Passant对此已有一个极好的解释,见his post)
显然,您不能总是改变您正在使用的对象(例如:为您生成字段的WinForms),因此您可能不得不回退使用临时变量。
答案 2 :(得分:0)
如果编组对象的另一侧已经死亡,它将抛出运行时错误,指出引用的对象不再存在。
答案 3 :(得分:0)
或者你可以写:
var obj = new Remotable();
Console.WriteLine(((int) obj.field).ToString()); // No warning
在这里,您自行负责该演员表(拆箱)。