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

17 .net warnings runtime marshalbyrefobject

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

我知道这个警告是什么,知道如何解决它.

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

Han*_*ant 28

你可能正在谈论警告CS1690,repro代码:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

在远程处理方案中,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
}
Run Code Online (Sandbox Code Playgroud)

  • 多谢。我刚刚收到此警告,很幸运您在这里找到了答案。 (2认同)

Sim*_*hid 5

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

public class Remotable : MarshalByRefObject {
    public int field;
}
Run Code Online (Sandbox Code Playgroud)

可能成为

public class Remotable : MarshalByRefObject {
    public int field { get; set }
}
Run Code Online (Sandbox Code Playgroud)

并且您不再收到任何警告!(对此,汉斯·帕桑特(Hans Passant)已有出色的解释,请参阅他的文章

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