Making a value type behave as a reference type using Expression>

后端 未结 3 1672
失恋的感觉
失恋的感觉 2021-01-21 02:41

We know that int is a value type and so the following makes sense:

int x = 3;
int y = x;
y = 5;
Console.WriteLine(x); //says 3. 

Now, here is a

相关标签:
3条回答
  • 2021-01-21 02:53

    Here is a full solution:

    // Credits to digEmAll for the following code
    public delegate void Setter<T>(T newValue);
    public delegate T Getter<T>();
    public class MagicPointer<T>
    {
        private Getter<T> getter;
        private Setter<T> setter;
    
        public T Value
        {
            get { return getter(); }
            set { setter(value); }
        }
    
        public MagicPointer(Getter<T> getter, Setter<T> setter)
        {
            this.getter = getter;
            this.setter = setter;
        }
    }
    
    // Code starting from here is mine
    public static class MagicUtilClass
    {
        public static MagicPointer<T> LinkVariable<T>(Expression<Func<T>> expression)
        {
            var memberExpr = expression.Body as MemberExpression;
            if (memberExpr == null)
                throw new InvalidOperationException("The body of the expression is expected to be a member-access expression.");
            var field = memberExpr.Member as FieldInfo;
            if (field == null)
                throw new InvalidOperationException("The body of the expression is expected to be a member-access expression that accesses a field.");
            var constant = memberExpr.Expression as ConstantExpression;
            if (constant == null)
                throw new InvalidOperationException("The body of the expression is expected to be a member-access expression that accesses a field on a constant expression.");
            return new MagicPointer<T>(() => (T) field.GetValue(constant.Value),
                                       x => field.SetValue(constant.Value, x));
        }
    }
    

    Usage:

    int x = 47;
    var magic = MagicUtilClass.LinkVariable(() => x);
    magic.Value = 48;
    Console.WriteLine(x);  // Outputs 48
    

    To understand why this solution works, you need to know that the compiler transforms your code quite considerably whenever you use a variable inside a lambda expression (irrespective of whether that lambda expression becomes a delegate or an expression tree). It actually generates a new class containing a field. The variable x is removed and replaced with that field. The Usage example will then look something like this:

    CompilerGeneratedClass1 locals = new CompilerGeneratedClass1();
    locals.x = 47;
    var magic = MagicUtilClass.LinkVariable(() => locals.x);
    // etc.
    

    The “field” that the code retrieves is the field containing x, and the “constant” that it retrieves is the locals instance.

    0 讨论(0)
  • 2021-01-21 03:03

    Integers in .NET are immutable. I'm not sure what problem you're trying to solve with this issue. Have you considered creating a class which has a property which "wraps" the integer? That class would be a reference types and what you're trying to achieve would not require any "magic" utility classes - just normal C# reference-type behavior.

    0 讨论(0)
  • 2021-01-21 03:10

    You can do something like this:

    public delegate void Setter<T>(T newValue);
    public delegate T Getter<T>();
    public class MagicPointer<T>
    {
        private Getter<T> getter;
        private Setter<T> setter;
    
        public T Value
        {
            get
            {
                return getter();
            }
            set
            {
                setter(value);
            }
        }
    
        public MagicPointer(Getter<T> getter, Setter<T> setter)
        {
            this.getter = getter;
            this.setter = setter;
        }
    
    }
    

    usage:

    int foo = 3;
    var pointer = new MagicPointer<int>(() => foo, x => foo = x);
    pointer.Value++;
    //now foo is 4
    

    Of course this solution doesn't guarantee a strong compile time control, because is up to the coder to write a good getter or setter.

    Probably, if you need something like a pointer you should reconsider your design, because likely you can do it in another way in C# :)

    0 讨论(0)
提交回复
热议问题