我知道这个问题已被提出,但这次我有两个额外的限制:
无法使用反射。
我不想在属性的setter周围传递一个包装器。我想通过setter本身:
// NO! NO! NO!
myObject.MyMethod(value => anotherObject.AnotherProperty = value);
答案 0 :(得分:11)
你的问题的答案是否定的。至少就C#而言。
为什么呢?假设您有以下对象:
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是一个声明为Foo->(int-> unit)的函数,它相当于一个返回委托void d(int value)的方法。实质上,如果需要,您可以使用另一个模块来破坏C#编译器约束。我选择F#只是因为我有编译器方便,但我敢打赌你也可以用C ++ / CLI做到这一点。
这个和包装器之间的主要区别在于,即使你仍然需要为你想要获得委托的每个类型编写一个包装器,该包装器最终不会附加到最终委托。< / p>
我不知道你的问题是什么是“没有反思”的限制 - 你是在一个禁止反思的环境中运行,还是你认为你的性能受限而你负担不起使用反射。如果它是后者,那么还有几个选项对你开放,它们提供了比简单反射更好的性能来获取属性集方法然后调用它(有效地通过名称调用)。
答案 1 :(得分:6)
定义此辅助函数(您需要添加错误检查):
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本身,没有包装器。
答案 2 :(得分:4)
为什么不使用界面?
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...
}
答案 3 :(得分:2)
C#不允许这样做(因为编译器在set_AnotherProperty方法上生成了specialname属性),但J#会因为它不支持属性语法。
这就是代码 的样子。
Action<int> x = set_AnotherProperty(1);
但是,C#编译器告诉你
Error 3 'Program.AnotherProperty.set':
cannot explicitly call operator or accessor
答案 4 :(得分:1)
我很确定答案是否定的。最常见的方法是使用lambda表达式,例如参见硬编码的INotifyPropertyChanged字符串的常见解决方案:
没有其他神奇的方法!
答案 5 :(得分:0)
如果你不想在属性的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);
它不漂亮,但它可以做你想做的事。
答案 6 :(得分:0)
对于您的约束,似乎您根本无法使用属性。如果性能考虑因素非常强大,以至于您无法简单地将setter包装在lambda中,那么您可能希望改变开发风格。这通常是在代码调优的极端情况下所做的。戴上你的Java帽子并在你的C#代码中创建显式的setter和getter方法:
class MyClass {
private string _value;
public void setProperty(string value) { _value = value; }
}
没有涉及任何属性......但是,真正通过lambdas购买了多少?我不认为这是显而易见的。
答案 7 :(得分:0)
如果知识纯度和词汇美不是同义词,那么属性可以包含在代理中。当然它不是一个属性,null是读/写操作的触发器,需要被调用&#39;毕竟,它就像一个功能一样对待它。
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 );