有没有任何方法可以将属性的setter传递给委托

本文关键字:setter 属性 方法 任何 有没有 | 更新日期: 2023-09-27 18:00:32

我知道这个问题已经被问过了,但这次我有两个额外的限制:

  1. 不能使用反射。

  2. 我不想在属性的setter周围传递包装。我想通过设置器本身:

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

有没有任何方法可以将属性的setter传递给委托

你的问题的答案是否定的。至少就C#而言。

为什么?假设您有以下对象:

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

您知道,在后台,编译器会自动为您生成int get_SomeProp()和void set_SomeProp(int值)(加上支持字段),所以您应该能够做到这一点:

var someMethod = foo.get_SomeProp;

你几乎可以。您从编译器中得到以下错误:"无法显式调用运算符或访问器"。所以,没有反射或包装,你就是SOL。大概我之所以这么说,也许是因为C#不允许将getter或setter视为真正的方法,并不意味着其他.NET语言不能。例如,我可以在F#中写这篇文章:

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

现在muckWithFoo是一个声明为Foo->(int->unit)的函数,它相当于一个返回委托void d(int值)的方法。本质上,如果需要的话,你可以使用另一个模块来打破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);

这并不完全是你想要的(它使用反射),但可能会尽可能接近。返回的委托是setter本身,而不是包装器。

为什么不使用接口?

interface IPropertySetter {
  string Property { set; }
}

让你的班级实现它:

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

并将其传递给您的对象:

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

没有其他神奇的方法了!

如果你不想传递一个围绕属性的setter的包装,你可以反过来让setter成为一个围绕方法的包装。例如:

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能给你买多少钱?我不认为这是显而易见的。

如果知识纯洁性和词汇美不是同义词,则属性可以封装在委托中。当然,它不是一个完全的属性,null是读/写操作的触发器,并且需要"called"将其视为函数。

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