Подтвердить что ты не робот

Строка parse для С# lambda Func

Есть ли способ преобразовать строковое представление lambda в lambda Func?

Func<Product, bool> func = Parse<Product, bool>("product => product.Name.Length > 0");

Я попробовал Dynamic LINQ, но он не работает должным образом - например, он не ожидает синтаксиса ambda = > .

Сводка ответов:

  • написание собственного С# -компилятора - очень забавно
  • запуск внешнего компилятора (например, csc.exe) - очень медленно
  • с помощью DLINQ - как я уже сказал, я не вижу, как он может анализировать выражения lambda

Зачем мне это нужно: потому что нет способа передать lambdas в пользовательские атрибуты, например

[Secure(role => role.CanDoThis && role.AllowedCount > 5)]

Итак, в качестве обходного пути я хотел бы передать лямбда как строку: "role = > role.CanDoThis & role.AllowedCount > 5". Но похоже, что мне придется использовать DLINQ следующим образом: "CanDoThis && AllowedCount > 5" - поскольку этот синтаксис он понимает. Но мой вопрос касался истинных лямбда, я уже использовал DLINQ во время запроса.

4b9b3361

Ответ 1

У них много парсеров лямбда-выражения. Некоторые из них Lambda-Parser, Sprache

Пример кода:

Пример1: строка concat и number рассчитывают:

string code = "2.ToString()+(4*2)"; // C# code Func<string> 
func = ExpressionParser.Compile<Func<string>>(code); // compile code 
string result = func(); // result = "28"

Ответ 2

Вы можете проанализировать строку и создать выражение лямбда, используя класс Expression, по существу дублирующий функцию компилятора.

Ответ 3

Думаю, вам нужно обратиться к CSharpCodeProvider. Однако рассмотрение всех возможных ссылок на локальные переменные может быть не тривиальным. И как бы вы сказали CSharpCodeProvider о типе параметра лямбда? Я бы, вероятно, создал класс шаблонов, выглядящий следующим образом:

class ExpressionContainer {
    public Expression<Func<Product, bool>> TheExpression;
    public string Length;

    public ExpressionContainer() {
        TheExpression = <user expression text>;
    }
}

Затем сделайте что-то вроде этого:

string source = <Code from above>;
Assembly a;
using (CSharpCodeProvider provider = new CSharpCodeProvider(...) {
    List<string> assemblies = new List<string>();
    foreach (Assembly x in AppDomain.CurrentDomain.GetAssemblies()) {
        try {
            assemblies.Add(x.Location);
        }
        catch (NotSupportedException) {
            // Dynamic assemblies will throw, and in .net 3.5 there seems to be no way of finding out whether the assembly is dynamic before trying.
        }
    }

    CompilerResults r = provider.CompileAssemblyFromSource(new CompilerParameters(assemblies.ToArray()) { GenerateExecutable = false, GenerateInMemory = true }, source);
    if (r.Errors.HasErrors)
        throw new Exception("Errors compiling expression: " + string.Join(Environment.NewLine, r.Errors.OfType<CompilerError>().Select(e => e.ErrorText).ToArray()));
    a = r.CompiledAssembly;
}
object o = a.CreateInstance("ExpressionContainer");
var result = ( Expression<Func<Product, bool>>)o.GetType().GetProperty("TheExpression").GetValue(o);

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

Ответ 4

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

Я так считаю, что LINQPad делает это.

Ответ 5

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

sealed class Product {
   public bool CanDoThis { get; set; }
   public int AllowedCount { get; set; }
}

public enum SecureFuncType {
   Type1,
   Type2,
   Type3
}

sealed class SecureAttribute : Attribute {
   [NotNull] readonly Func<Product, bool> mFunc;

   public SecureAttribute(SecureFuncType pType) {
      var secureFuncs = new Dictionary<SecureFuncType, Func<Product, bool>> {
         { SecureFuncType.Type1, role => role.CanDoThis && role.AllowedCount > 1 },
         { SecureFuncType.Type2, role => role.CanDoThis && role.AllowedCount > 2 },
         { SecureFuncType.Type3, role => role.CanDoThis && role.AllowedCount > 3 }
      };

      mFunc = secureFuncs[pType];
   }
}

[Secure(SecureFuncType.Type1)]
sealed class TestClass {
}

// etc...