2010-09-06 75 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#:)

相關問題