Създайте анонимен метод от низ в c#

възможно ли е да се създаде анонимен метод в c# от низ?

напр. ако имам низ "x + y * z" възможно ли е да превърна това в някакъв метод/ламбда обект, който мога да извикам с произволни x,y,z параметри?


person toasteroven    schedule 15.10.2009    source източник
comment
Почти съм сигурен, че някой човек от Microsoft направи предаване за това, че .net 4 разполага с компилатор като услуга за постигане на неща като това. Не знам обаче дали е вашият случай.   -  person Alex Bagnolini    schedule 16.10.2009
comment
Андерс говореше за работа в много далечно бъдеще, когато говореше за компилатора като услуга в PDC. C# 4 със сигурност няма да има такава работа.   -  person Eric Lippert    schedule 16.10.2009
comment
Дубликат: stackoverflow .com/questions/1437964/ Този въпрос също получи по-конкретен отговор от този.   -  person Joren    schedule 19.10.2009


Отговори (5)


Възможно е, да. Трябва да анализирате низа и например да компилирате делегат, като използвате изразни дървета.

Ето пример за създаване на (x, y, z) => x + y * z с помощта на изразни дървета:

ParameterExpression parameterX = Expression.Parameter(typeof(int), "x");
ParameterExpression parameterY = Expression.Parameter(typeof(int), "y");
ParameterExpression parameterZ = Expression.Parameter(typeof(int), "z");
Expression multiplyYZ = Expression.Multiply(parameterY, parameterZ);
Expression addXMultiplyYZ = Expression.Add(parameterX, multiplyYZ);
Func<int,int,int,int> f = Expression.Lambda<Func<int, int, int, int>>
(
    addXMultiplyYZ,
    parameterX,
    parameterY,
    parameterZ
).Compile();
Console.WriteLine(f(24, 6, 3)); // prints 42 to the console
person jason    schedule 15.10.2009
comment
+1 Това е добър пример, но е съобразен с въпросния низ. Този пример не би помогнал на OP да анализира произволен низ, да изведе типовете идентификатори в низа и да създаде метод въз основа на това, което е намерено. Въпреки това, +1 за вас за добрия пример. - person Andrew Hare; 16.10.2009
comment
Предположих, че синтактичният анализ ще бъде по-познат (поне литературата за синтактичния анализ е по-голяма) на OP, отколкото изразните дървета. Целта на изразното дърво е единствено да му покаже технологията и да покаже силата им, а не да реши общия проблем. - person jason; 16.10.2009

Само за забавление с помощта на CodeDom (всеки валиден C# код е разрешен в низа, стига да присъства в mscorlib (изобщо няма проверка за грешки):

static class Program
{
    static string code = @"
        public static class __CompiledExpr__
        {{
            public static {0} Run({1})
            {{
                return {2};
            }}
        }}
        ";

    static MethodInfo ToMethod(string expr, Type[] argTypes, string[] argNames, Type resultType)
    {
        StringBuilder argString = new StringBuilder();
        for (int i = 0; i < argTypes.Length; i++)
        {
            if (i != 0) argString.Append(", ");
            argString.AppendFormat("{0} {1}", argTypes[i].FullName, argNames[i]);
        }
        string finalCode = string.Format(code, resultType != null ? resultType.FullName : "void",
            argString, expr);

        var parameters = new CompilerParameters();
        parameters.ReferencedAssemblies.Add("mscorlib.dll");
        parameters.ReferencedAssemblies.Add(Path.GetFileName(Assembly.GetExecutingAssembly().Location));
        parameters.GenerateInMemory = true;

        var c = new CSharpCodeProvider();
        CompilerResults results = c.CompileAssemblyFromSource(parameters, finalCode);
        var asm = results.CompiledAssembly;
        var compiledType = asm.GetType("__CompiledExpr__");
        return compiledType.GetMethod("Run");
    }

    static Action ToAction(this string expr)
    {
        var method = ToMethod(expr, new Type[0], new string[0], null);
        return () => method.Invoke(null, new object[0]);
    }

    static Func<TResult> ToFunc<TResult>(this string expr)
    {
        var method = ToMethod(expr, new Type[0], new string[0], typeof(TResult));
        return () => (TResult)method.Invoke(null, new object[0]);
    }

    static Func<T, TResult> ToFunc<T, TResult>(this string expr, string arg1Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T) }, new string[] { arg1Name }, typeof(TResult));
        return (T arg1) => (TResult)method.Invoke(null, new object[] { arg1 });
    }

    static Func<T1, T2, TResult> ToFunc<T1, T2, TResult>(this string expr, string arg1Name, string arg2Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T1), typeof(T2) },
            new string[] { arg1Name, arg2Name }, typeof(TResult));
        return (T1 arg1, T2 arg2) => (TResult)method.Invoke(null, new object[] { arg1, arg2 });
    }

    static Func<T1, T2, T3, TResult> ToFunc<T1, T2, T3, TResult>(this string expr, string arg1Name, string arg2Name, string arg3Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T1), typeof(T2), typeof(T3) },
            new string[] { arg1Name, arg2Name, arg3Name }, typeof(TResult));
        return (T1 arg1, T2 arg2, T3 arg3) => (TResult)method.Invoke(null, new object[] { arg1, arg2, arg3 });
    }

    static void Main(string[] args)
    {
        var f = "x + y * z".ToFunc<int, int, long, long>("x", "y", "z");
        var x = f(3, 6, 8);

    }
}
person Julien Roncaglia    schedule 15.10.2009

C# няма никаква функционалност като тази (други езици - като JavaScript - имат eval функции за обработка на неща като тези). Ще трябва сами да анализирате низа и да създадете метод с изразни дървета или чрез излъчване на IL.

person Andrew Hare    schedule 15.10.2009

Има функционалност за това в рамката .Net.

Не е лесно. Трябва да добавите някакъв код около израза, за да го превърнете в цялостен сборник, включително клас и метод, които можете да извикате.

След това предавате низа на

CSharpCodeProvider.CompileAssemblyFromSource(options, yourcode);

Ето пример

person adrianm    schedule 15.10.2009
comment
Заедно с намирането на необходимите параметри и т.н. Трудно е да се направи приятелски с CompileAssemblyFromSource. - person user7116; 16.10.2009

Може да е възможно с граматика (напр. ANTLR) и интерпретатор, който създава изразни дървета. Това не е малка задача, но можете да успеете, ако ограничите обхвата на това, което приемате като вход. Ето някои препратки:

Ето как може да изглежда код за трансформиране на ANTLR ITtree в Expression дърво. Не е завършен, но ви показва срещу какво се изправяте.

private Dictionary<string, ParameterExpression> variables
    = new Dictionary<string, ParameterExpression>();

public Expression Visit(ITree tree)
{
    switch(tree.Type)
    {
        case MyParser.NUMBER_LITERAL:
            {
                float value;
                var literal = tree.GetChild(0).Text;
                if (!Single.TryParse(literal, out value))
                    throw new MyParserException("Invalid number literal");
                return Expression.Constant(value);
            }

        case MyParser.IDENTIFIER:
            {
                var ident = tree.GetChild(0).Text;
                if (!this.variables.ContainsKey(ident))
                {
                    this.variables.Add(ident,
                       Expression.Parameter(typeof(float), ident));
                }

                return this.variables[ident];
            }

        case MyParser.ADD_EXPR:
            return Expression.Add(Visit(tree.GetChild(0)), Visit(tree.GetChild(1)));

        // ... more here
    }
}
person user7116    schedule 15.10.2009