Идентификация списка "действий" (какие свойства будут назначены) в действии <t> класс

70
5

Английский не мой родной язык, я надеюсь, что сделаю свой вопрос достаточно ясным.

Я хотел бы знать, как определить, какие действия содержатся в классе Action (точно, какие свойства будут вызываться) при выполнении.

Представьте себе простой класс сотрудников...

public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}

... и простой метод Update(), принимающий Action <> как параметр

public static void Update(Action<Employee> action) {
//HERE : how could i know which properties will be assigned by analyzing the Action<Employee> object ?

Employee em = new Employee();

action(em);

//Only FirstName and LastName have been assigned to "em"
}

Метод Update можно вызвать следующим образом:

//Call the Update method with only two "actions" : assign FirstName and LastName properties.
Update(e => { e.FirstName = "firstname"; e.LastName = "lastname"; });

Моя проблема заключается в том, чтобы идентифицировать внутри метода Update(), какие свойства были "запланированы" для назначения (и связанных значений) внутри объекта Action <>.

Как я могу обнаружить, проанализировав объект Action <>, что только свойства FirstName и LastName будут назначены значениями "firstname" и "lastname"? Это вообще возможно?

Я не могу найти помощь в google и SO на этом. Возможно, я задаю вопрос не так.

В конце сообщения отображается вся программа, которая может быть выполнена/отлажена.

Спасибо заранее всем здесь, на SO.

Майк

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestAction
{
class Program
{
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}

static void Main(string[] args)
{
//Call the Update method with only two "actions" : assign FirstName and LastName properties.
Update(e => { e.FirstName = "firstname"; e.LastName = "lastname"; });
}

public static void Update(Action<Employee> action)
{
//HERE : how could i know which properties will be assigned by analyzing the Action<Employee> object ?

Employee em = new Employee();

action(em);

//Only FirstName and LastName have been assigned to "em"
}
}
}

спросил(а) 2013-06-28T10:30:00+04:00 7 лет, 3 месяца назад
1
Решение
57

Это непростая задача для решения, но позвольте мне изложить, как я знаю, как это сделать, если вам действительно нужно это делать.

Разберите и интерпретируйте

Это будет связано с получением дизассемблера.NET (или x86/x64), который вы запускаете во время выполнения, указывая его на рассматриваемый метод и затем анализируя вывод, ища доступ к полям целевого типа.

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

Использовать прокси-объект, который регистрирует доступ

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

Это намного проще сделать, вам нужно будет сделать свой класс виртуальным, а каждое свойство также виртуальным, а затем создать класс потомков, который вы можете использовать с помощью Reflection.Emit или другими способами создания типов во время выполнения.

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

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

Мое мнение

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

ответил(а) 2013-06-28T10:38:00+04:00 7 лет, 3 месяца назад
57

Спасибо всем за то, что я понял, что делегаты не могут быть проанализированы (без ваших объяснений я все еще пытался выяснить, как...).

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

public static void Update<T>(Expression<Action<T>> action) where T : class

Внутри метода Update выражение анализируется стандартным шаблоном Visitor: я могу получить назначенные свойства и связанные значения.

Метод называется так:

Update<Employee>(o => new Employee() { FirstName = "Mike", Email = "mike@so.com" });

В очередной раз благодарим за помощь !

Майк

ответил(а) 2013-06-28T19:58:00+04:00 7 лет, 3 месяца назад
57

Вы не можете увидеть, что было изменено каким-то встроенным способом, поскольку изменилось отношение текущего состояния вашего экземпляра. Пример:

A {Name="Patrick"} //init 
A {Name = "Jane"} // changed from Patrick to Jane
A {Name = "Martin"} //changed from Jane to Martin

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


При вызове " Update прокрутите список имен измененных свойств, получите только их значение и обновите их (все через отражение). После успешного обновления обновите список.

Просто идея, но могут быть и другие способы добиться этого.

ответил(а) 2013-06-28T10:38:00+04:00 7 лет, 3 месяца назад
41

Вы не можете анализировать делегатов. Для анализа вам понадобится дерево выражений:

public static void Update(Expression<Action<Employee>> action)

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

ответил(а) 2013-06-28T10:37:00+04:00 7 лет, 3 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема