.netruntimewarningsmarshalbyrefobject

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


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?


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
    }