-2

I have the following lambda that works fine:

public interface IOhlcv : ITick
{
    decimal Open { get; set; }
    decimal High { get; set; }
    decimal Low { get; set; }
    decimal Close { get; set; }
    decimal Volume { get; set; }
}

c1,c2,c3 are of type IList<IOhlcv>

This is the lambda :

(c1, c2, c3) => c1.Close / c2.Close * c3.Close

If the lambda were a string representation of a lambda,

string lambdaStr = "(c1, c2, c3) => c1.Close / c2.Close * c3.Close"

how do I compile it to a real lambda? NOTE: (lambdaStr can be created dynamically, e.g., c1, c2, c3 can be any number of parameters with operators between them)

I tried using https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples#expr

Like this:

var options = ScriptOptions.Default.AddReferences(typeof(IOhlcv).Assembly);
var projection = await CSharpScript.EvaluateAsync<IList<decimal>>(projectionString, options);

but I can't get it to work:

Message = "(1,1): error CS1660: Cannot convert lambda expression to type 'IList<decimal>' because it is not a delegate type"

Edit 1

Even trying this

try
        {
            string lambdaStr = "(c1, c2, c3) => c1.Close / c2.Close * c3.Close";
            var options = ScriptOptions.Default.AddReferences(typeof(IOhlcv).Assembly);
            // this will be a delegate type and you will need to turn it into one that fits
            dynamic projection = await CSharpScript.EvaluateAsync<dynamic>(lambdaStr, options);

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

give this exception:

'Microsoft.CodeAnalysis.Scripting.CompilationErrorException' in Microsoft.CodeAnalysis.Scripting.dll
Microsoft.CodeAnalysis.Scripting.CompilationErrorException: (1,1): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type
   at Microsoft.CodeAnalysis.Scripting.ScriptBuilder.ThrowIfAnyCompilationErrors(DiagnosticBag diagnostics, DiagnosticFormatter formatter)
   at Microsoft.CodeAnalysis.Scripting.ScriptBuilder.CreateExecutor[T](ScriptCompiler compiler, Compilation compilation, Boolean emitDebugInformation, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1.GetExecutor(CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1.RunAsync(Object globals, Func`2 catchException, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync[T](String code, ScriptOptions options, Object globals, Type globalsType, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync[T](String code, ScriptOptions options, Object globals, Type globalsType, CancellationToken cancellationToken)
   at Trady.Form1.<ZipSeries>d__17.MoveNext() in C:\Users\idf\Form1.cs:line 281

Edit 2

This suggestion feels like something is getting closer, but no cigar:

   try
    {
        var options = ScriptOptions.Default.AddReferences(typeof(IOhlcv).Assembly);
        var projection = await CSharpScript.EvaluateAsync<Func<IOhlcv,IOhlcv,IOhlcv,decimal>>(projectionString, options);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

ex = {"(1,18): error CS1061: 'IOhlcv' does not contain a definition for 'close' and no accessible extension method 'close' accepting a first argument of type 'IOhlcv' could be found (are you missing a using directive or an assembly reference?)"}

Edit 3

There was a bug in code where ".close" is generated. When it is changed to ".Close" the code in Edit 2 works.

It still doesn't answer the question of how to create a lambda that can take a variable number of parameters since Func<IOhlcv,IOhlcv,IOhlcv,decimal> is hardwired, but it is a step forward.

Can CSharpScript.EvaluateAsync compile to a delegate that takes a variable number of parameters?

Ivan
  • 7,448
  • 14
  • 69
  • 134
  • The error message is right, what you get back from compiling that is not a list, it is a lambda, where did you get `IList` from? – Lasse V. Karlsen Apr 16 '19 at 16:52
  • 1
    Try `EvaluateAsync>(...)`, but I think you need to provide more information to the compiler, like `"(IOhlcv c1, IOhlcv c2, IOhlcv c3) => ..."`. – Lasse V. Karlsen Apr 16 '19 at 16:53
  • See Edit 2 in original post. – Ivan Apr 16 '19 at 17:03
  • I had a bug in the string. Changed .close to .Close. Your suggestion works! – Ivan Apr 16 '19 at 17:10
  • 1
    @LasseVågsætherKarlsen That still doesn't work dynamically with any number of inputs like OP is going for – Maximilian Burszley Apr 16 '19 at 17:12
  • TheIncorregible you are correct, but this is a huge step forward for me. I think instead of a hardwired function like func I need a delegate(param object[]). But I don't know if that can be cast to a lambda, let alone passed to EvaluateAsync, since I need to pass it to List.Zip() – Ivan Apr 16 '19 at 17:12
  • See Edit 3 in original post. – Ivan Apr 16 '19 at 17:27
  • @Ivan, did you ever find a solution for passing a variable number of parameters? I'm trying to solve a very similar problem. – Krid Nov 27 '19 at 11:01
  • I am trying to solve a similar problem. I did find the following NON-open source library that seems to solve it: [C# Eval Expression](https://eval-expression.net/), for up to 9 parameters at least. More specifically, see: [Eval.Compile](https://eval-expression.net/eval-compile) – Smolakian May 20 '21 at 18:14

1 Answers1

0

It's not evaluating the lambda.. It's just converting the string TO a lambda, you still need to execute it yourself:

string lambdaStr = "(c1, c2, c3) => c1.Close / c2.Close * c3.Close";
var options = ScriptOptions.Default.AddReferences(typeof(IOhlcv).Assembly);
// this will be a delegate type and you will need to turn it into one that fits
dynamic projection = await CSharpScript.EvaluateAsync<dynamic>(lambdaStr, options);

// or similar
System.Console.WriteLine(projection(a, b, c));
Maximilian Burszley
  • 18,243
  • 4
  • 34
  • 63