Создайте действие , чтобы «установить» свойство, когда мне предоставляется выражение LINQ для «get»,

Я хотел бы иметь возможность сгенерировать скомпилированное выражение для установки свойства, учитывая выражение lambda, которое предоставляет метод «get» для свойства.

Вот что я ищу:

public Action CreateSetter(Expression<Func> getter) { // returns a compiled action using the details of the getter expression tree, or null // if the write property is not defined. } 

Я все еще пытаюсь понять различные типы classов Expression, поэтому, если вы можете указать мне в правильном направлении, это было бы здорово.

Используя ответ @ Ani в качестве отправной точки, вы можете использовать следующее для генерации скомпилированного выражения .

 [TestMethod] public void CreateSetterFromGetter() { Action ageSetter = InitializeSet((Person p) => p.Age); Action nameSetter = InitializeSet((Person p) => p.Name); Person p1 = new Person(); ageSetter(p1, 29); nameSetter(p1, "John"); Assert.IsTrue(p1.Name == "John"); Assert.IsTrue(p1.Age == 29); } public class Person { public int Age { get; set; } public string Name { get; set; } } public static Action InitializeSet(Expression> getter) { PropertyInfo propertyInfo = (getter.Body as MemberExpression).Member as PropertyInfo; ParameterExpression instance = Expression.Parameter(typeof(TContainer), "instance"); ParameterExpression parameter = Expression.Parameter(typeof(TProperty), "param"); return Expression.Lambda>( Expression.Call(instance, propertyInfo.GetSetMethod(), parameter), new ParameterExpression[] { instance, parameter }).Compile(); } 

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

Вы могли бы, конечно, пройти дерево выражений, а затем использовать Delegate.CreateDelegate для создания соответствующего Action< ,> . Это довольно просто, за исключением всех проверок проверки (я не уверен, охватил ли я все):

Я не эксперт дерева выражений, но я не думаю, что создание дерева выражений, а затем вызов Compile возможно здесь, поскольку деревья выражений не могут содержать операторов присваивания, насколько я знаю. ( EDIT : По-видимому, они были добавлены в .NET 4. Это сложная функция, поскольку компилятор C #, похоже, не может создать их из lambdas).

 public static Action CreateSetter (Expression> getter) { if (getter == null) throw new ArgumentNullException("getter"); var memberEx = getter.Body as MemberExpression; if (memberEx == null) throw new ArgumentException("Body is not a member-expression."); var property = memberEx.Member as PropertyInfo; if (property == null) throw new ArgumentException("Member is not a property."); if(!property.CanWrite) throw new ArgumentException("Property is not writable."); return (Action) Delegate.CreateDelegate(typeof(Action), property.GetSetMethod()); } 

Использование :

 public class Person { public int Age { get; set; } } ... static void Main(string[] args) { var setter = CreateSetter((Person p) => p.Age); var person = new Person(); setter(person, 25); Console.WriteLine(person.Age); // 25 } 

Обратите внимание, что это создает открытый делегат экземпляра, что означает, что он не связан с каким-либо конкретным экземпляром TContaining . Его просто изменить, привязав к конкретному экземпляру; вам также необходимо передать TContaining а затем использовать другую перегрузку Delegate.CreateDelegate . Подпись метода будет выглядеть примерно так:

 public static Action CreateSetter (Expression> getter, TContaining obj) 

Указатели только я боюсь (я не на ПК) – но;

  • Лэмбда. Боди скорее всего будет MemberExpression
  • сделать безопасный .Member ( as т. д.) и получить доступ к .Member
  • поскольку вы верите в это свойство, это должно быть PropertyInfo, поэтому test / cast и т. д.
  • из PropertyInfo вызовите GetSetMethod (), чтобы получить соответствующий MethodInfo
  • используйте Delegate.CreateDelegate, чтобы получить это как делегат (передав тип действия)
  • наконец, отбросить делегата на ожидаемый тип делегата