在C#中动态创建数组的方法

时间:2018-06-06 14:15:41

标签: c# .net scripting

首先,我在.Net方面没有多少经验 - 尤其是在过去的7年里。

我正在尝试开发一个应用程序,并希望合并另一个库(https://github.com/Giorgi/Math-Expression-Evaluator

该库允许我评估像Evaluate("a+b", a: 1,b: 1)这样的数学表达式。方法签名为public decimal Evaluate(string expression, object argument = null)

  1. 我想更好地了解.Net如何将逗号分隔的参数转换为单个"参数"。
  2. 我不确定如何动态创建该参数...例如,迭代值列表并创建一个与该签名的相应参数匹配的对象。
  3. 我真的只是寻找文档和更多信息的指针..感谢您的一切。

    编辑:对不起......故意留下它广泛,因为我不是在寻找人为我做的工作......我似乎无法找到自己研究的起点。

    该方法称为

    dynamic engine = new ExpressionEvaluator() ; 
    engine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6)) ; 
    

    在Evalute()的主体中是这段代码(它将该参数转换为字符串字典,十进制对。

    if (argument == null)
            {
                return new Dictionary<string, decimal>();
            }
    
            var argumentType = argument.GetType();
    
            var properties = argumentType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => p.CanRead && IsNumeric(p.PropertyType));
    
            var arguments = properties.ToDictionary(property => property.Name,
                property => Convert.ToDecimal(property.GetValue(argument, null)));
    
            return arguments;
    

    我希望能够解析的字符串类似于&#34; a:1,b:2&#34;并将其转换为与Evaluate()签名匹配的对象。

1 个答案:

答案 0 :(得分:11)

该库正在使用高级魔法......非常高级: - )

诀窍是该类声明为:

public class ExpressionEvaluator : DynamicObject

因此,它是一个实现.NET 4.0中引入的dynamic魔法的类

现在......在课堂上有两种Evaluate方法:

public decimal Evaluate(string expression, object argument = null)

private decimal Evaluate(string expression, Dictionary<string, decimal> arguments)

通常可见且可用的唯一方法是第一种方法。它用作:

engine.Evaluate("a + b + c", new { a = 1, b = 2, c = 3 });

new { ... }创建一个匿名对象,然后通过使用反射Dictionary<string, decimal>将其“解压缩”here,将其输入private Evaluate()

如果您尝试使用其他表示法,请使用:

engine.Evaluate("a + b + c", a: 1, b: 2, c: 3 });

然后.NET无法将该方法与存在的public Evaluate()匹配,但是作为DynamicObject的子类的类会导致C#编译器编写一些“魔术”代码,启动this方法(仍然由ExpressionEvaluator实现):

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)

首先检查我们要调用Evaluate:

if (nameof(Evaluate) != binder.Name)

如果我们尝试调用Evaluate,则会将参数解压缩到new Dictionary<string, decimal>(),然后调用private Evaluate()

作为旁注,要使用“动态”写作方式Evaluate,您必须声明engine变量;

dynamic dynamicEngine = new ExpressionEvaluator();

所以使用dynamic变量类型。

现在......在编写库时,您可以:

  • 使用匿名对象,问题是匿名对象必须在编译时定义其“形状”(因此在编译时您必须知道需要a,{{1如果你没有在编译时创建b,那么在运行时不需要c。}和d。例如,请参阅我在三年前提供的关于如何在运行时创建动态匿名类型的response。我为该代码块提供的原因之一是:

      

    .NET框架的某些部分大量使用反射来渲染对象(例如所有各种数据网格)。这些部分与动态对象不兼容,通常不支持object []。解决方案通常是将数据封装在DataTable中......或者您可以使用: - )

    请注意,在该回复的其中一条评论中,有一个链接指向由Dynamic.Linq的众多实现之一使用的代码的修改版本。

  • 使用非匿名对象(new { a, b, c, d })。该库不区分匿名和非匿名对象。与以前一样的限制,因为在编译时你需要一个具有正确参数数量的new Foo { a = 1, b = 2 c = 3 }

  • 使用Foo表示法。可悲的是,即使这是非常静态的。您无法轻松添加新参数,必须在编译时定义“变量”的数量和名称。

一种可能的解决方案是修改源代码(它是单个文件)并使dynamic这个方法:

public

然后您可以轻松动态地填充private decimal Evaluate(string expression, Dictionary<string, decimal> arguments)