18

访问 Form 上的成员可能会导致运行时异常,因为它是 marshal-by-reference 类的字段

我知道这个警告是什么并且知道如何解决它。

我的问题是为什么这会导致运行时错误?

4

4 回答 4

32

您可能在谈论警告 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
}
于 2010-11-14T18:56:02.110 回答
10

除了@hans-passant 的建议之外,我认为解决此警告的另一种有用方法是将您的字段转换为属性。

public class Remotable : MarshalByRefObject {
    public int field;
}

可能成为

public class Remotable : MarshalByRefObject {
    public int field { get; set }
}

并且您不再收到任何警告!(Hans Passant 对此已经有了很好的解释,请参阅他的帖子

显然,您不能总是更改您正在使用的对象(例如:为您生成字段的 WinForms),因此您可能不得不回退到使用临时变量。

于 2015-10-27T09:17:25.593 回答
3

或者你可以写:

var obj = new Remotable();

Console.WriteLine(((int) obj.field).ToString());     // No warning

在这里,您对演员表负责(拆箱)。

于 2017-10-01T07:31:08.647 回答
1

如果编组对象的另一端已经死亡,它将抛出一个运行时错误,指出引用的对象不再存在。

于 2010-11-14T18:53:03.517 回答