簡體   English   中英

有沒有辦法將屬性的設置器傳遞給委托?

[英]Is there any way to pass the setter of a property to a delegate?

我知道這個問題已經被問過了,但是這次我有兩個額外的限制:

  1. 不能使用反射。

  2. 我不想在屬性的設置器周圍傳遞一個包裝器。 我想通過設置器本身:

     // NO. NO. NO; myObject.MyMethod(value => anotherObject.AnotherProperty = value);

你的問題的答案是否定的。 至少就 C# 而言。

為什么? 假設您有以下 object:

public class Foo
{
    public int SomeProp { get; set; }
}

你知道編譯器會自動為你生成 int get_SomeProp() 和 void set_SomeProp(int value) (加上支持字段),所以你應該能夠做到這一點:

var someMethod = foo.get_SomeProp;

你可以 - 幾乎。 您從編譯器收到此錯誤:“無法顯式調用運算符或訪問器”。 所以沒有反射或包裝,你就是 SOL。 也許。 我說可能是因為 C# 不允許您將 getter 或 setter 視為真正的方法,這並不意味着其他一些 .NET 語言不能。 例如,我可以在 F# 中這樣寫:

namespace PassSetter

module mucker =
    let muckWithFoo (aFoo:Foo) =
        aFoo.set_SomeProp

現在 muckWithFoo 是一個 function 聲明為 Foo->(int->unit),相當於返回委托 void d(int value) 的方法。 本質上,如果需要,您可以使用另一個模塊來打破 C# 編譯器約束。 我選擇 F# 只是因為我有方便的編譯器,但我敢打賭你也可以使用 C++/CLI 來做到這一點。

這與包裝器之間的主要區別在於,即使您仍然需要為您希望從中獲取委托的每種類型編寫一個包裝器,該包裝器最終也不會附加到最終委托。

我不知道您對“無反射”約束的問題是什么 - 您是在一個禁止反射的環境中運行,還是您認為您的性能受限以至於您無法使用反射. 如果是后者,那么您可以使用更多選項來提供比簡單地反射來獲取屬性集方法然后調用它(有效地按名稱調用)更好的性能。

定義此輔助函數(您需要添加錯誤檢查):

Action<TObject,TValue> GetSetter<TObject,TValue>(Expression<Func<TObject,TValue>> property)
{
    var memberExp=(MemberExpression)property.Body;
    var propInfo=(PropertyInfo)memberExp.Member;
    MethodInfo setter=propInfo.GetSetMethod();
    Delegate del=Delegate.CreateDelegate(typeof(Action<TObject,TValue>),setter);
    return (Action<TObject,TValue>)del;
}

並像這樣使用它:

Action<MyClass,int> setter=GetSetter((MyClass o)=>o.IntProperty);

這不完全是您想要的(它使用反射),但可能與您得到的一樣接近。 返回的委托是設置器本身,沒有包裝器。

為什么不使用接口?

interface IPropertySetter {
  string Property { set; }
}

讓您的 class 實現它:

class MyClass : IPropertySetter {
  public string Property { get; set; }
}

並將其傳遞給您的 object:

var c = new MyClass();
myObject.MyMethod(c);

...

void MyMethod(IPropertySetter setter) {
  setter.Property = someValue;
  // you can also store the "setter" and use it later...
}

C# 不允許這樣做(因為編譯器在 set_AnotherProperty 方法上生成了 specialname 屬性),但是 J# 會因為它不支持屬性語法。

這就是代碼樣子。

  Action<int> x = set_AnotherProperty(1);

然而,C# 編譯器告訴你

Error   3   'Program.AnotherProperty.set': 
cannot explicitly call operator or accessor

我很確定答案是否定的。 最常見的方法是使用 lambda 表達式,例如硬編碼 INotifyPropertyChanged 字符串的常見解決方案:

http://10rem.net/blog/2010/12/16/strategies-for-improving-inotifypropertychanged-in-wpf-and-silverlight

沒有其他神奇的方法!

如果知識純度和詞匯美不是同義詞,則可以將屬性包裝在委托中。 當然,這不是一個屬性,畢竟 null 是讀/寫操作的觸發器,並且需要“被調用”將其視為 function。

xType1 xProperty1 {get;set;}

xType2 xProperty1 {get;set;}

xCallingFunction()
{
   ....
   xCalledFunction( 
     ((s)=> s == null ? xProperty1 : (xProperty1 = s)),
     ((s)=> s == null ? xProperty2 : (xProperty2 = s))
     );
   ....
}

....

xCalledFunction( Func<xType, xType> parm1, Func<xType2, xType2> parm2 )
   var p1 = parm1(null);
   parm2( newValue );
   parm1( parm1(null) + 16 );

如果您不想在屬性的設置器周圍傳遞包裝器,則可以 go 反過來,讓設置器成為方法的包裝器。 例如:

public MyClass
{
    private Foo _myProperty;
    public Foo MyProperty
    {
        get { return _myProperty; }
        set { MyPropertySetter(value); }
    }

    public void MyPropertySetter(Foo foo)
    {
        _myProperty = foo;
    }
}

然后做:

myObject.Mymethod(myOtherObject.MyPropertySetter);

它不漂亮,但它做了你想做的事。

對於您的限制,您似乎根本無法使用屬性。 如果性能考慮如此強烈以至於您不能簡單地將 setter 包裝在 lambda 中,那么您可能想要改變您的開發風格。 這通常是在代碼調整的極端情況下所做的。 戴上您的 Java 帽子並在您的 C# 代碼中創建顯式 setter 和 getter 方法:

class MyClass {
  private string _value;
  public void setProperty(string value) { _value = value; } 
} 

不涉及任何屬性......但是,與 lambdas 相比,它真正能買到多少? 我不認為這很明顯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM