I am trying to use the new Roslyn
scripting modules. This is an example of how to use it. Notice that Globals
appears to need to be a class
.
public class Globals
{
public int X;
public int Y;
}
var globals = new Globals { X = 1, Y = 2 };
Console.WriteLine(await CSharpScript.EvaluateAsync<int>("X+Y", globals: globals));
I have a generic function
that takes a type T
, with the length of the array indeterminate (but relatively small length in most cases):
void Func<T>()
{
T[] values;
}
How do I convert the T[]
to an anonymous type
?
So if I have T
if of type decimal
and in this case values
is of length 3,
values[0] = 124.3, values[1] = 132.4, values[2] = 23
I would like to have an anonymous type
created that looks something like this:
var v = new { v1 = 124.3, v2 = 232.4, v3 = 23 };
Is this possible? That is, to create an anonymous type from an array that you don't know the length of at compile time?
NOTE: This is why I need an anonymous type and not a tuple, or a List etc. And, since I don't know how big the array is, I can't hard wire a class
Edit 1
I was somewhat shocked when I tried the solution given below that this even compiles:
dynamic v = new ExpandoObject();
var dictionary = (IDictionary<string, object>)v;
dictionary.Add("X", 1.5);
dictionary.Add("Y", 2.7);
//var globals = new Globals { X = 1.5, Y = 2.7 };
var retval = CSharpScript.EvaluateAsync<double>("System.Math.Sqrt(System.Math.Log(X + Y))",
globals: dictionary).GetAwaiter();
//retval = (decimal)Convert.ChangeType(retval, typeof(decimal));
Console.WriteLine(retval.GetResult());
Sadly, I get a runtime error:
Microsoft.CodeAnalysis.Scripting.CompilationErrorException
HResult=0x80131500
Message=(1,34): error CS0103: The name 'X' does not exist in the current context
Source=Microsoft.CodeAnalysis.Scripting
StackTrace:
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.InitializeScriptEngineAsync() in C:\Users\idf\Form1.cs:line 79
at Form1..ctor() in C:\Users\idf\Form1.cs:line 56
at Trady.Program.Main() in C:\Users\idf\Program.cs:line 19
The problem is that anonymous types are autogenerated types and they're fixed at compiletime. So it's a statically typed dynamic type.
ExpandoObject is the object that you use with the dynamic keyword in order to add properties and methods dynamically.
Here is an example for your function:
void Func<T>()
{
T[] values;
dynamic v = new ExpandoObject();
var dictionary = (IDictionary<string, object>)v;
var i = 0;
foreach (var value in values)
{
i++;
dictionary.Add($"v{i}", value);
}
}
The ExpandoObject implements the IDictionary Interface and as such can be cast to it, in order to add properties dynamically.
You can use a dictionary of values by converting the dictionary into a string of variable declaration code that executes before the equation code. The following sample handles numeric data types and strings. The GetDeclaration method can be customized to support other data types such as DateTime or custom classes.
private static void Main()
{
// Declare a dictionary with desired variables
var values = new Dictionary<string, object>
{
{ "X", (int) 1 },
{ "Y", (decimal) 2 },
{ "flag", true },
{ "option", "test" }
};
// Convert variables into code declarations
string declareValues = string.Join(" ", values.Select(v => GetDeclaration(v.Key, v.Value)));
// declareValues = System.Int32 X = 1; System.Decimal Y = 2; System.Boolean flag = true; System.String option = "test";
string code = "X + Y";
// Run the variable declaration code before the equation code
var evalResult = CSharpScript.EvaluateAsync(declareValues + code).Result;
// evalResult = (decimal) 3
Console.WriteLine($"Variables: {declareValues}");
Console.WriteLine($"{code} = {evalResult}");
}
private static string GetDeclaration(string name, object value)
{
var type = value.GetType();
string valueCode;
if (value is string)
{
valueCode = $"@\"{((string)value).Replace("\"", "\"\"")}\"";
}
else if (value is bool)
{
valueCode = value.ToString().ToLower();
}
else
{
valueCode = value.ToString();
}
return $"{type} {name} = {valueCode};";
}
User contributions licensed under CC BY-SA 3.0