从<t>数组构建匿名类型

时间:2019-04-18 20:12:50

标签: c# anonymous-types c#-7.0

我正在尝试使用新的Roslyn脚本模块。这是如何使用它的示例。请注意,Globals似乎需要是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));

我有一个类型为generic function的{​​{1}},数组的长度是不确定的(但在大多数情况下,长度较小):

T

如何将void Func<T>() { T[] values; } 转换为T[]

因此,如果我有anonymous type类型的T,并且在这种情况下decimal的长度为3,

values

我想创建一个看起来像这样的values[0] = 124.3, values[1] = 132.4, values[2] = 23

anonymous type

这可能吗?也就是说,要从您在编译时不知道其长度的数组创建匿名类型?

注意:这就是为什么我需要匿名类型而不是元组或List等的原因。而且,由于我不知道数组的大小,所以我无法对类进行硬连接

编辑1

当我尝试下面给出的甚至可以编译的解决方案时,我有些震惊:

var v = new { v1 = 124.3, v2 = 232.4, v3 = 23 };

可悲的是,我遇到了运行时错误:

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());

2 个答案:

答案 0 :(得分:1)

问题在于匿名类型是自动生成的类型,并且在编译时已得到修复。因此,这是一个静态类型的动态类型。

ExpandoObject是您与dynamic关键字一起使用以动态添加属性和方法的对象。

以下是您的函数示例:

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);
    }
}

ExpandoObject实现IDictionary接口,因此可以将其强制转换为动态添加属性。

答案 1 :(得分:0)

通过将字典转换为在等式代码之前执行的变量声明代码的字符串,可以使用值的字典。下面的示例处理数字数据类型和字符串。可以自定义GetDeclaration方法以支持其他数据类型,例如DateTime或自定义类。

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};";
}