2010-09-06 41 views
3

我们知道,int是一个值类型,所以下面是有道理的制作价值型表现为引用类型由于缺乏更好的术语“链接”,两个变量指向相同的存储位置。使用表达式<Func<T>>

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

问题是:linkVariable的方法是怎么样的?它会是什么样的返回类型?

虽然,我题为出任制作值类型表现为引用类型,上述linkVariable方法适用于引用类型太..,即

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

我不知道如何实现这在C#中(不允许使用不安全的代码)?

欣赏想法。谢谢。

+1

似乎非常重复[这个问题](http:// stackoverflow。COM /问题/ 1962539 /使用性关闭到保跟踪的-A-变好主意,或者脏了帽子戏法)。 – GSerg 2010-09-06 19:39:14

+0

@GSerg - 感谢您的链接,但这是另一回事。当然,改变和绑定在lambda中的变量的值会改变评估lambda的结果。我感兴趣的是在另一件事上捕获价值,以便当另一件事情被修改时,原来的变化,即当y被改变时,x被改变。 – 2010-09-06 20:08:13

回答

2

这里是一个完整的解决方案:

// 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 

要unders为什么这个解决方案能够工作,您需要知道,无论您在lambda表达式中使用变量(不管该lambda表达式是变为委托还是表达式树),编译器都会相当大地转换您的代码。它实际上会生成一个包含字段的新类。变量x被删除并替换为该字段。然后,用例将是这个样子:

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

的“场”,该代码检索是包含X领域,“恒定”,它获取的是当地人实例。

+0

完美。谢谢。 – 2010-09-06 20:36:09

0

.NET中的整数是不可变的。我不确定你在解决这个问题时遇到了什么问题。你有没有考虑创建一个具有“包装”整数属性的类?该类将是一个引用类型,您试图实现的内容不需要任何“魔术”实用程序类 - 只是普通的C#引用类型行为。

2

你可以做这样的事情:

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

} 

用法:

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

当然,这种解决方案并不能保证一个强大的编译时间的控制,因为要由编码器写好的吸气剂或二聚物。

也许,如果你需要的东西就像一个指针,你应该重新考虑你的设计,因为很可能你能做到以另一种方式在C#:)

相关问题