Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Methods for dynamically creating an array in C#

Tags:

c#

.net

scripting

First, I don't have much experience in .Net - especially within the last 7 years.

I'm trying to develop an application and would to incorporate another library (https://github.com/Giorgi/Math-Expression-Evaluator)

That library allows me to evaluate math expressions like Evaluate("a+b", a: 1,b: 1). The method signature is public decimal Evaluate(string expression, object argument = null)

  1. I would like to understand better how .Net translates comma-separated arguments into a single "argument".
  2. I'm not sure how to create that argument dynamically.. for example, iterating through a list of values and creating an object that will match the appropriate argument for that signature.

I'm really just looking for pointers for documentation and more information.. Thanks for anything.

EDIT: Sorry.. purposely left it broad because I wasn't looking for people to do my work for me.. just can't seem to find a starting point to do my own research.

The method is called like

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

In the body of Evalute() is this code (which turns that argument into a Dictionary of String, Decimal pairs.

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;

What I'd like to be able to do is parse a String like "a:1,b:2" and turn it into an object that matches that Evaluate() signature.

like image 581
Greg Avatar asked Jun 06 '18 14:06

Greg


People also ask

How do you create a dynamic array?

Dynamic arrays in C++ are declared using the new keyword. We use square brackets to specify the number of items to be stored in the dynamic array. Once done with the array, we can free up the memory using the delete operator. Use the delete operator with [] to free the memory of all array elements.

What is dynamic array How is it created?

A dynamic array is a random access, variable-size list data structure that allows elements to be added or removed. It is supplied with standard libraries in many modern programming languages. Dynamic arrays overcome a limit of static arrays, which have a fixed capacity that needs to be specified at allocation.

Can we create a dynamic array?

A simple dynamic array can be constructed by allocating an array of fixed-size, typically larger than the number of elements immediately required.

How do you dynamically add elements to an array?

There are two ways to dynamically add an element to the end of a JavaScript array. You can use the Array. prototype. push() method, or you can leverage the array's “length” property to dynamically get the index of what would be the new element's position.


1 Answers

That library is using high level magic... Very high level :-)

The trick is that the class is declared as:

public class ExpressionEvaluator : DynamicObject

So it is a class that implements the dynamic magic introduced in .NET 4.0

Now... In the class there are two Evaluate methods:

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

and

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

The only method normally visible and usable is the first one. It is used like:

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

The new { ... } creates an anonymous object, that is then "unpacked" here through the use of reflection to a Dictionary<string, decimal> to be fed to the private Evaluate().

If you try to use the other notation, the one like:

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

then the .NET can't match the method to the public Evaluate() that is present, but the class, being a subclass of DynamicObject, causes the C# compiler to write some "magic" code that launches this method (that is still implemented by the ExpressionEvaluator):

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

That first checks that we want to call Evaluate:

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

and if we are trying to call Evaluate, it unpacks the parameters to a new Dictionary<string, decimal>() and then calls the private Evaluate().

As a sidenote, to use the "dynamic" way of writing Evaluate you have to declare the engine variable like;

dynamic dynamicEngine = new ExpressionEvaluator();

So using the dynamic variable type.

Now... As the library is written you can:

  • Use an anonymous object, with the problem that anonymous objects must have their "shape" defined at compile time (so at compile time you must know that you will need a a, a b and a c. You can't need a d at runtime if you didn't create a new { a, b, c, d } at compile time). See for example a response I gave three years ago about how to create dynamic anonymous types at runtime. One of the reasons I gave for that block of code was:

    there are parts of the .NET framework that heavily use reflection to render objects (for example all the various datagrids). These parts are incompatible with dynamic objects and often don't support object[]. A solution is often to encapsulate the data in a DataTable... or you can use this :-)

    Note that in one of the comments to that response there is a link to a modified version of my code used by one of the many implementations of Dynamic.Linq.

  • Use a non-anonymous object (a new Foo { a = 1, b = 2 c = 3 }). The library doesn't make distinctions between anonymous and non-anonymous objects. So same limitation as before, because at compile time you need a Foo class with the right number of parameters

  • Use the dynamic notation. Sadly even that is quite static. You can't easily add new parameters, that for the number and name of the "variables" must be defined at compile time.

A possible solution is to modify the source code (it is a single file) and make public this method:

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

then you can easily and dynamically populate the Dictionary<string, decimal> arguments

like image 193
xanatos Avatar answered Sep 28 '22 04:09

xanatos