Есть ли способ передать сеттер свойства делегату?

Я знаю, что этот вопрос уже задан, но на этот раз у меня есть еще два ограничения:

  • Отражение нельзя использовать.

  • Я не хочу передавать обертку вокруг средства настройки свойств. Я хочу передать сам сеттер:

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

Ответ 1

Ответ на ваш вопрос будет отрицательным. По крайней мере, что касается С#.

Почему? Скажем, у вас есть следующий объект:

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

вы знаете, что под капотом компилятор будет автоматически генерировать int get_SomeProp() и void set_SomeProp (int value) для вас (плюс поле поддержки), чтобы вы могли это сделать:

var someMethod = foo.get_SomeProp;

и вы можете - почти. Вы получаете эту ошибку от компилятора: "нельзя явно вызвать оператор или аксессуар". Таким образом, без отражения или обертки, вы SOL. Может быть. Я говорю, может быть, потому, что только потому, что С# не позволяет вам рассматривать геттер или сеттер как настоящий метод, это не значит, что какой-то другой язык .NET не может. Например, я могу записать это в F #:

namespace PassSetter

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

и теперь muckWithFoo - это функция, объявленная как Foo → (int- > unit), которая эквивалентна методу, который возвращает делегат void d (значение int). В сущности, вы можете использовать другой модуль, чтобы разорвать ограничение компилятора С#, если это необходимо. Я выбрал F # только потому, что у меня есть компилятор, но я готов поспорить, что вы могли бы это сделать и с С++/CLI.

Основное различие между этим и оболочкой состоит в том, что даже если вам все равно нужно написать оболочку для каждого типа, для которого вы хотите получить делегат, эта оболочка не привязана к окончательному делегату.

Я не знаю, какова ваша проблема с ограничением "без отражения" - это то, что вы работаете в среде, которая запрещает размышления или что вы считаете, что вы настолько ограничены в производительности, что не можете себе позволить использовать отражение. Если это последнее, то для вас доступно еще несколько вариантов, которые дают вам гораздо лучшую производительность, чем просто отражение, чтобы получить метод набора свойств, а затем вызвать его (фактически вызвать по имени).

Ответ 2

Определите эту вспомогательную функцию (вам нужно добавить проверку ошибок):

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

Это не совсем то, что вы хотите (оно использует отражение), но, вероятно, как можно ближе. Возвращаемый делегат - это сам сеттер, без оболочки.

Ответ 3

Почему бы не использовать интерфейс?

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

Ответ 4

С# не позволяет этого (из-за атрибута specialname, который генерирует компилятор по методу set_AnotherProperty), однако J # будет потому, что он не поддерживает синтаксис свойства.

Вот как выглядит код.

  Action<int> x = set_AnotherProperty(1);

Однако компилятор С# сообщает вам

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

Ответ 5

Я уверен, что ответ отрицательный. Наиболее распространенный подход к этому заключается в использовании лямбда-выражений, см., Например, общее решение для жестко закодированных строк INotifyPropertyChanged:

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

Нет других волшебных способов!

Ответ 6

Если вы не хотите передавать обертку вокруг средства определения свойств, вы можете пойти наоборот и позволить сеттеру быть оберткой вокруг метода. Например:

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

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

то do:

myObject.Mymethod(myOtherObject.MyPropertySetter);

Это не очень, но он делает то, что вы хотите сделать.

Ответ 7

Для ваших ограничений кажется, что вы вообще не можете использовать свойства. Если соображения производительности настолько сильны, что вы не можете просто обернуть сеттер в лямбда, тогда вы можете изменить свой стиль разработки. Обычно это делается в крайних случаях настройки кода. Наденьте шляпу Java и создайте явные методы setter и getter в коде С#:

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

Никаких свойств не было... Но насколько вы действительно покупаете вас за лямбда? Я бы не подумал, что это заметно.

Ответ 8

Свойство может быть завернуто в делегат, если интеллектуальная чистота и лексическая красота не являются синонимами. Конечно, это не совсем свойство, при этом null является триггером для операций чтения/записи и необходимость "вызова" обрабатывать его как функцию. В конце концов.

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