制作一个值类型的行为作为使用防爆pression℃的引用类型;函数功能< T>> [英] Making a value type behave as a reference type using Expression<Func<T>>

查看:109
本文介绍了制作一个值类型的行为作为使用防爆pression℃的引用类型;函数功能< T>>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们知道,int是一个值类型,所以下面是有道理的:

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. 

现在,这里是一个有点code,我们要为缺乏一个更好的词链接两个变量指向同一个内存位置。

Now, here is a bit of code where we want to for lack of a better term "link" the two variables point to the same memory location.

int x = 3;
var y = MagicUtilClass.linkVariable(() => x);
y.Value = 5;
Console.WriteLine(x) //says 5.

现在的问题是:怎样的方法linkVariable什么样子的?那会是什么的返回类型是什么样子?

虽然,我题为出任制作值类型的行为为引用类型,该linkVariable方法适用于引用类型太..,即

Although, I titled the post as making a value type behave as a reference type, the said linkVariable method works for reference types too.., i.e,

Person x = new Person { Name = "Foo" };
var y = MagicUtilClass.linkVariable(() => x);
y.Value = new Person { Name = "Bar" };
Console.WriteLine(x.Name) //says Bar.

我不知道如何在C#中实现这一点(不允许的方式使用不安全code)?

I am not sure how to achieve this in C# (not allowed to use unsafe code by the way)?

鸭preciate想法。谢谢你。

Appreciate ideas. Thanks.

推荐答案

下面是一个完整的解决方案:

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

用法:

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

要理解为什么这个解决方案有效,您需要知道,编译器将你的code颇大无论你使用一个变量中的lambda EX pression(不论是否对拉姆达EX pression成为委托或前pression树)。它实际上生成包含一个字段的新类。变量的 X 的被删除,并与现场更换。用法例子则是这个样子:

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.

田的code检索是包含的 X 的领域,恒,它获取的是当地人的实例。

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

这篇关于制作一个值类型的行为作为使用防爆pression℃的引用类型;函数功能&LT; T&GT;&GT;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆