14

我有一个方法可以根据传递给它的操作委托更改“帐户”对象:

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 类型委托,我对表达式树等的了解还不够好,无法弄清楚如何实现我的目标想。

有没有一种简单的方法可以做我想做的事,还是我需要正确学习表达式并编写大量代码?

(我想这样做的原因是允许从用户可以指定 lambda 表达式来执行更改的 powershell 脚本批量更新帐户对象的简单方法。)

4

4 回答 4

4

动态 LINQ 库是一个不错的选择,因为它会生成表达式,您可以以轻量级方式编译成代码。

您提供的示例实际上产生了一个布尔值——因此您应该能够请求一个 Func 并且它可能会对其进行排序。

编辑:这当然是错误的,因为表达式中根本没有赋值。

因此,另一种可能的方法是采用两个 lambda。一个是找到你想要的属性,一个是提供一个值:

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

然后使用反射将第一个 lambda 中引用的属性设置为第二个 lambda 的结果。Hackish,但与调用 C# 编译器相比,它仍然可能是轻量级的。

这样你就不必自己做很多代码生成了——你得到的表达式将包含你需要的大部分内容。

于 2009-04-03T17:23:32.283 回答
3

你可以试试这个:Dynamic Lambda Expressions Using An Isolated AppDomain

它使用 CodeDOM 编译器编译 lambda 表达式。为了处理创建的内存中程序集,编译器在隔离的AppDomain. 对于通过域边界传递表达式,它必须被序列化。唉,Expression<>不是Serializable。因此,必须使用一个技巧。所有细节都在帖子中解释。

顺便说一句,我是那个组件的作者。我非常想听听您的反馈。

于 2009-09-05T06:39:17.667 回答
1

在没有完整编译的情况下,没有通用的方法可以将字符串解析为 lambda 表达式,因为 lambda 表达式可以引用在 lambda 表达式之外定义的内容。我知道没有库可以处理您想要的特定案例。在 C# 讨论组的一个线程上对此进行了长时间的讨论。

获得所需内容的最简单方法是在运行时编译方法。您可以编写一个接收字符串“a.Enabled = true; return a;”的函数 并将其粘贴在以 Account 作为参数的函数的中间。我会使用这个作为起点,但您也可以使用另一个线程上提到的函数。

于 2009-04-03T17:04:35.137 回答
0

这很容易:

  • 使用CodeDom生成包含您将用于构建表达式的“周围类”的模块;此类必须实现您的应用程序已知的接口
  • 使用CodeSnippedExpression将表达式注入其成员。
  • 使用Activator类型在运行时创建此类的实例。

基本上,您需要使用 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>在生成的类型中。

于 2009-07-05T12:00:38.070 回答