【问题标题】:Build anonymous type from an array of <T>从 <T> 数组构建匿名类型
【发布时间】:2019-04-18 20:12:49
【问题描述】:

我正在尝试使用新的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,它采用T 类型,数组的长度不确定(但在大多数情况下长度相对较小):

void Func<T>()
{
   T[] values;
}

如何将T[] 转换为anonymous type

所以如果我有T 如果类型为decimal 并且在这种情况下values 的长度为3,

values[0] = 124.3, values[1] = 132.4, values[2] = 23

我想创建一个看起来像这样的anonymous type

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

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

注意:这就是为什么我需要匿名类型而不是元组或 List 等的原因。而且,由于我不知道数组有多大,所以我不能硬连线一个类强>

编辑 1

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

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

很遗憾,我收到了运行时错误:

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

【问题讨论】:

  • 索引与我无关。只是解决方案。
  • 你考虑过List&lt;T&gt;吗?
  • 查看编辑后的帖子了解为什么我需要匿名类型。
  • 为什么数组本身不能是“全局”包装器上的命名属性,例如:CSharpScript.EvaluateAsync&lt;int&gt;("v[0]+v[1]", globals: new { v = new [] { 1, 2 }}).GetAwaiter()
  • github.com/dotnet/roslyn/wiki/Scripting-API-Samples#chain 对你有用吗(如果你只有简单的值可能没问题)...否则可能与 stackoverflow.com/questions/3862226/… 重复

标签: c# anonymous-types c#-7.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。
【解决方案2】:

您可以通过将字典转换为在方程式代码之前执行的变量声明代码字符串来使用值字典。以下示例处理数字数据类型和字符串。可以自定义 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};";
}

【讨论】:

    猜你喜欢
    • 2012-04-06
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多