0

I'm trying to create a MethodCallExpression to a method, that takes a parameter by reference (and may modify this parameter). This Expression is part of a ExpressionTree, that gets compiled to a delegate later.

For non-readonly fields all works as expected, but when I pass in a readonly field, the compiler somehow passes the field by value and changing the parameter doesn't affect the readonly field. The object from which the readonly field comes can be a struct or object.

How can I trick the compiler to pass the readonly field as an actual reference?

I know, that it supposed to be readonly, but with reflection, you also can modify the field. (even with structs: FieldInfo.SetValueDirect(__makeref(...), value))

Example Code:

internal class Program
{
    private struct Foo
    {
        private readonly int MyInt;

        public Foo(int myInt)
        {
            MyInt = myInt;
        }
    }

    public static void IncrementInt(ref int i)
    {
        i++;
    }

    private delegate void RefAction<T>(ref T parameter);

    private static void Main(string[] args)
    {
        var field = typeof(Foo).GetField("MyInt", BindingFlags.NonPublic | BindingFlags.Instance);
        var method = typeof(Program).GetMethod(nameof(IncrementInt));

        var parameter = Expression.Parameter(typeof(Foo).MakeByRefType());
        var call = Expression.Call(method, Expression.MakeMemberAccess(parameter, field));
        var deleg = Expression.Lambda<RefAction<Foo>>(call, parameter).Compile();

        var foo = new Foo(0);
        deleg(ref foo);
    }
}
FlaXxy
  • 1
  • 1
  • 1
    It sounds like you are trying to do something inherently wrong. – Guru Stron May 08 '20 at 09:54
  • 1
    `readonly` is there for a reason... the object probably doesn't expect the values to change and could show unintended behavior. You don't need to trick the compiler, it's there to help you, not annoy you – Biesi May 08 '20 at 10:05
  • When your access a `readonly` field, the compiler makes a protective copy of the value from the field. That way the compiler protects the readonly field from accidentally, or in this case intentionally, changes. So no, you can't do that. – JonC May 08 '20 at 10:22
  • @GuruStron I want to create a Serializer and populate the fields of a deserialized field. That may be an inherently wrong thing to do. – FlaXxy May 08 '20 at 10:52
  • @BiesiGrr There are areas, where even .Net needs to trick the runtime to create objects without calling the constructor or setting private or readonly fields from the outside with reflection. (FormatterServices) – FlaXxy May 08 '20 at 10:55
  • @FlaXxy then, i think, you should try looking at `Reflection.Emit`, not expression trees. – Guru Stron May 08 '20 at 11:14
  • @GuruStron `ILgenerator.Emit` does work, thx. How can I integrate that into Expression Trees? – FlaXxy May 08 '20 at 14:02
  • @FlaXxy I think you should look at [`MethodBuilder`](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.methodbuilder?redirectedfrom=MSDN&view=netcore-3.1) to dynamically create an method and pass it then to expression tree as you will do with ordinary one. But, TBH, never done that myself. – Guru Stron May 08 '20 at 14:11

0 Answers0