Есть ли способ передать установщик свойства делегату? - PullRequest
11 голосов
/ 28 июня 2011

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

  1. Отражение не может быть использовано.

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

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

Ответы [ 8 ]

11 голосов
/ 28 июня 2011

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

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

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

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

var someMethod = foo.get_SomeProp;

и вы можете - почти. Вы получаете эту ошибку от компилятора: «не может явно вызвать оператор или метод доступа». Так что без размышлений или обертки, вы SOL. Может быть. Я говорю, может быть, потому что только потому, что C # не позволяет вам обращаться с геттером или сеттером как с реальным методом, это не значит, что какой-то другой язык .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.

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

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

6 голосов
/ 29 июня 2011

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

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

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

4 голосов
/ 28 июня 2011

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

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...
}
2 голосов
/ 28 июня 2011

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

Вот как будет выглядеть код .

  Action<int> x = set_AnotherProperty(1);

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

Error   3   'Program.AnotherProperty.set': 
cannot explicitly call operator or accessor
1 голос
/ 28 июня 2011

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

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

Других волшебных путей нет!

0 голосов
/ 10 декабря 2012

Свойство может быть заключено в делегат, если интеллектуальная чистота и лексическая красота не являются синонимами.Конечно, это не совсем свойство, поскольку 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 );
0 голосов
/ 28 июня 2011

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

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

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

0 голосов
/ 28 июня 2011

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

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

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

...