Accessing a member on Form may cause a runtime exception because it is a field of a marshal-by-reference class

StackOverflow https://stackoverflow.com/questions/4178576

Question

Accessing a member on Form may cause a runtime exception because it is a field of a marshal-by-reference class

I know what this warning is and know how to solve it.

My question is why could this cause a runtime error?

Was it helpful?

Solution

You are probably talking about warning CS1690, repro code:

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());
    }
}

In a remoting scenario, the Test.Run method will work with a proxy of the Remotable object. Building a proxy for a property, method or event isn't much of a problem, just a matter of creating a MethodTable that contains the substitutes. Fields are a problem however, there's nothing to 'hook'. For a MBRO, the JIT compiler no longer generates code to access the field directly, it injects a call to a helper method built into the CLR, JIT_GetField32() in this case.

That helper checks if the object is a proxy and uses the remoting plumbing to obtain the remote value if that's the case. Or just accesses the field directly if it isn't. Making the ToString() call however requires the value to be boxed. That's a problem, boxing isolates the value from the proxy. There is no way to ensure that the boxed value is always an accurate copy of the remoted value. Calling JIT_GetField32() again whenever the ToString() method uses the value to format the string isn't possible.

The workaround for CS1690 is simple, beyond wrapping the field with a property, just copy the field value in a local variable. Now it is crystal clear that the code is working with a copy and there is never a surprise so the compiler won't have to emit a warning.

public static void Run() {
    var obj = new Remotable();
    var value = obj.field;
    Console.WriteLine(value.ToString());     // No warning
}

OTHER TIPS

In addition to the suggestion from @hans-passant, I think another useful way to fix this warning is by turning your field into a property.

public class Remotable : MarshalByRefObject {
    public int field;
}

could become

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

and you no longer get any warnings! (Hans Passant already has an excelent explanation for this, see his post)

Obviously, you can not always alter the object you are working with (example: WinForms where the fields are generated for you) so you might have to fallback to using a temporary variable.

If the other side of the marshalled object has died, it will throw a runtime error stating that the referenced object does not exist anymore.

Or you can write:

var obj = new Remotable();

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

Here you take your own responsibility for that cast (unboxing).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top