有没有任何方法可以将属性的setter传递给委托
本文关键字:setter 属性 方法 任何 有没有 | 更新日期: 2023-09-27 18:00:32
我知道这个问题已经被问过了,但这次我有两个额外的限制:
-
不能使用反射。
-
我不想在属性的setter周围传递包装。我想通过设置器本身:
// NO! NO! NO! myObject.MyMethod(value => anotherObject.AnotherProperty = value);
你的问题的答案是否定的。至少就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 );