简体   繁体   English

有没有办法将属性的设置器传递给委托?

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

I know this question has already been asked, but this time I have two additional constraints:我知道这个问题已经被问过了,但是这次我有两个额外的限制:

  1. Reflection cannot be used.不能使用反射。

  2. I don't want to pass a wrapper around a property's setter.我不想在属性的设置器周围传递一个包装器。 I want to pass the setter itself:我想通过设置器本身:

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

The answer to your question is going to be no.你的问题的答案是否定的。 At least as far as C# is concerned.至少就 C# 而言。

Why?为什么? Let's say that you have the following object:假设您有以下 object:

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

you know that under the hood the compiler will autogenerate int get_SomeProp() and void set_SomeProp(int value) for you (plus the backing field), so you should be able to do this:你知道编译器会自动为你生成 int get_SomeProp() 和 void set_SomeProp(int value) (加上支持字段),所以你应该能够做到这一点:

var someMethod = foo.get_SomeProp;

and you can - almost.你可以 - 几乎。 You get this error from the compiler: "cannot explicitly call operator or accessor".您从编译器收到此错误:“无法显式调用运算符或访问器”。 So without reflection or a wrapper, you're SOL.所以没有反射或包装,你就是 SOL。 Maybe.也许。 I say maybe because just because C# doesn't let you treat a getter or setter like a real method, it doesn't mean that some other .NET language can't.我说可能是因为 C# 不允许您将 getter 或 setter 视为真正的方法,这并不意味着其他一些 .NET 语言不能。 For example, I can write this in F#:例如,我可以在 F# 中这样写:

namespace PassSetter

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

and now muckWithFoo is a function declared as Foo->(int->unit), which is equivalent to a method that returns delegate void d(int value).现在 muckWithFoo 是一个 function 声明为 Foo->(int->unit),相当于返回委托 void d(int value) 的方法。 In essence, you can use another module to break the C# compiler constraint, if need be.本质上,如果需要,您可以使用另一个模块来打破 C# 编译器约束。 I picked F# only because I have the compiler handy, but I bet you could do this with C++/CLI as well.我选择 F# 只是因为我有方便的编译器,但我敢打赌你也可以使用 C++/CLI 来做到这一点。

The main difference between this and a wrapper is that even though you still need to write a wrapper for every type for which you would like to get a delegate from, that wrapper doesn't end up attached to the final delegate.这与包装器之间的主要区别在于,即使您仍然需要为您希望从中获取委托的每种类型编写一个包装器,该包装器最终也不会附加到最终委托。

I don't know what your issue is with the "no reflection" constraint - is it that you're running in an environment that forbids reflection or that you believe that you're so performance bound that you can't afford to use reflection.我不知道您对“无反射”约束的问题是什么 - 您是在一个禁止反射的环境中运行,还是您认为您的性能受限以至于您无法使用反射. If it's the latter, then there are several more options open to you that give you much better performance than simply reflection to get the property set method and then invoke it (effectively call by name).如果是后者,那么您可以使用更多选项来提供比简单地反射来获取属性集方法然后调用它(有效地按名称调用)更好的性能。

Define this helper function(You'll need to add the error checking):定义此辅助函数(您需要添加错误检查):

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

And use it like this:并像这样使用它:

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

This is not exactly what you want(It uses reflection), but probably as close as you'll get.这不完全是您想要的(它使用反射),但可能与您得到的一样接近。 The returned delegate is the setter itself, no wrapper.返回的委托是设置器本身,没有包装器。

Why not use an interface?为什么不使用接口?

interface IPropertySetter {
  string Property { set; }
}

Have your class implement it:让您的 class 实现它:

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

And pass it to your object:并将其传递给您的 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# does not allow this (because of the specialname attribute the compiler generates on the set_AnotherProperty method), however J# would because it does not support the property syntax. C# 不允许这样做(因为编译器在 set_AnotherProperty 方法上生成了 specialname 属性),但是 J# 会因为它不支持属性语法。

This is what the code would look like.这就是代码样子。

  Action<int> x = set_AnotherProperty(1);

However, the C# compiler tells you然而,C# 编译器告诉你

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

I am pretty sure the answer is no.我很确定答案是否定的。 The most common approach to this is to use lambda expressions, see for example the common solution to hard-coded INotifyPropertyChanged strings:最常见的方法是使用 lambda 表达式,例如硬编码 INotifyPropertyChanged 字符串的常见解决方案:

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

There are no other magic ways!没有其他神奇的方法!

A property can be wrapped in a delegate, if intellectual purity and lexical beauty are not synonymous.如果知识纯度和词汇美不是同义词,则可以将属性包装在委托中。 Of course it is not quite a property, with null being a trigger for read/write operations and the need for the 'called' to treat it like a function, after all.当然,这不是一个属性,毕竟 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 );

If you don't want to pass a wrapper around a property's setter, you can go the other way around and let the setter be a wrapper around a method.如果您不想在属性的设置器周围传递包装器,则可以 go 反过来,让设置器成为方法的包装器。 For example:例如:

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

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

then do:然后做:

myObject.Mymethod(myOtherObject.MyPropertySetter);

It's not pretty, but it does what you want done.它不漂亮,但它做了你想做的事。

For your constraints, it seems you can't use properties at all.对于您的限制,您似乎根本无法使用属性。 If the performance considerations are so strong that you can't simply wrap the setter in a lambda, then you might want to change your style of development.如果性能考虑如此强烈以至于您不能简单地将 setter 包装在 lambda 中,那么您可能想要改变您的开发风格。 That's normally what's done in extreme cases of code tuning.这通常是在代码调整的极端情况下所做的。 Put on your Java hat and create explicit setter and getter methods in your C# code :戴上您的 Java 帽子并在您的 C# 代码中创建显式 setter 和 getter 方法:

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

No properties involved... But, how much that really buys you over lambdas?不涉及任何属性......但是,与 lambdas 相比,它真正能买到多少? I wouldn't think it's noticeable.我不认为这很明显。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM