Есть ли простой способ разобрать строку (лямбда-выражение) в делегат Action? - PullRequest
13 голосов
/ 03 апреля 2009

У меня есть метод, который изменяет объект «Account» на основе переданного в него делегата действия:

public static void AlterAccount(string AccountID, Action<Account> AccountAction) {
  Account someAccount = accountRepository.GetAccount(AccountID);
  AccountAction.Invoke(someAccount);
  someAccount.Save();
}

Это работает как задумано ...

AlterAccount("Account1234", a => a.Enabled = false);

... но теперь я хотел бы попробовать такой метод:

public static void AlterAccount(string AccountID, string AccountActionText) {
  Account someAccount = accountRepository.GetAccount(AccountID);
  Action<Account> AccountAction = MagicLibrary.ConvertMagically<Action<Account>>(AccountActionText);
  AccountAction.Invoke(someAccount);
  someAccount.Save();
}

Затем его можно использовать как:

AlterAccount("Account1234", "a => a.Enabled = false");

для отключения учетной записи «Account1234».

Я посмотрел на динамическую библиотеку запросов linq , которая, кажется, делает более или менее то, что я хочу, но для делегатов типа Func, и мои знания деревьев выражений и т. Д. Не очень хороши достаточно, чтобы понять, как добиться того, чего я хочу.

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

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

Ответы [ 4 ]

4 голосов
/ 03 апреля 2009

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

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

Редактировать: Это, конечно, неправильно, поскольку в выражениях вообще нет присваивания.

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

(a => a.AccountId), (a => true)

Затем используйте отражение, чтобы установить свойство, на которое ссылается первая лямбда, с результатом второй. Хакш, но он все еще, вероятно, легче по сравнению с вызовом компилятора C #.

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

3 голосов
/ 05 сентября 2009

Вы можете попробовать это: Динамические лямбда-выражения с использованием изолированного домена приложения

Компилирует лямбда-выражение с использованием компилятора CodeDOM. Чтобы утилизировать созданную сборку в памяти, компилятор работает на изолированном AppDomain. Для передачи выражения через границу домена оно должно быть сериализовано. Увы, Expression<> не Serializable. Таким образом, хитрость должна быть использована. Все подробности объяснены в посте.

Кстати, я автор этого компонента. Я бы очень хотел услышать ваше мнение об этом.

1 голос
/ 03 апреля 2009

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

Самый простой способ получить то, что вы хотите - это скомпилировать метод во время выполнения. Вы можете написать функцию, которая принимает строку «a.Enabled = true; return a;» и вставляет это в середину функции, которая принимает учетную запись в качестве параметра. Я бы использовал эту библиотеку в качестве отправной точки, но вы также можете использовать функцию, упомянутую в другом потоке .

0 голосов
/ 05 июля 2009

Это просто:

  • Используйте CodeDom , чтобы сгенерировать модуль, содержащий "окружающий класс", который вы будете использовать для построения выражения; этот класс должен реализовывать интерфейс, известный вашему приложению
  • Используйте CodeSnippedExpression , чтобы вставить выражение в его член.
  • Используйте Activator type для создания экземпляра этого класса во время выполнения.

По сути, вам нужно построить следующий класс с CodeDom:

using System;
using MyNamespace1;
using ...
using MyNamespace[N];

namespace MyNamespace.GeneratedTypes 
{
  public class ExpressionContainer[M] : IHasAccountAction
  {
    public Action<Account> AccountAction { 
      get {
        return [CodeSnippedExpression must be used here];
      }
    } 
  }
}

Предполагая, что IHasAccountAction:

public IHasAccountAction {
  public Action<Account> AccountAction { get; }
}

Если это сделано, вы можете легко получить выражение, скомпилированное из строки. Если вам нужно представление дерева выражений, используйте Expression<Action<Account>> вместо Action<Account> в сгенерированном типе.

...