Методы динамического создания массива в C#

Во-первых, у меня мало опыта работы с .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. Я не уверен, как создать этот аргумент динамически... например, перебирая список значений и создавая объект, который будет соответствовать соответствующему аргументу для этой подписи.

Я действительно просто ищу указатели на документацию и дополнительную информацию. Спасибо за все.

РЕДАКТИРОВАТЬ: Извините.. намеренно оставил его широким, потому что я не искал людей, которые выполняли бы мою работу за меня.. просто не могу найти отправную точку для проведения собственного исследования.

Метод называется так

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;

Что я хотел бы сделать, так это проанализировать строку типа «a: 1, b: 2» и превратить ее в объект, соответствующий этой подписи Evaluate().


person Greg    schedule 06.06.2018    source источник
comment
Даже вопрос 1 слишком широк и содержит много шагов. Не упомянутый вопрос 2. Поймите лучше означает, что вы уже что-то понимаете. Так что вы понимаете, а что нет?   -  person HimBromBeere    schedule 06.06.2018
comment
Это довольно интересно... new { ... } подразумевается в пункте 1?   -  person xanatos    schedule 06.06.2018
comment
Я даже не знаю, что вопрос №1 означает. Перевод аргументов, разделенных запятыми, в один аргумент?   -  person    schedule 06.06.2018
comment
Если я сам что-то не понимаю в С#, я не вижу, как пример использования Evaluate() вообще соответствует этой сигнатуре метода.   -  person David    schedule 06.06.2018
comment
Пункт 1 будет ниже спецификаций языка (С#) и компилятора, а не .Net, поскольку это всего лишь фреймворк.   -  person musefan    schedule 06.06.2018
comment
Ах нет... Класс динамический и динамически обрабатывает использование метода... увлекательно   -  person xanatos    schedule 06.06.2018
comment
Взглянув на библиотеку, похоже, что метод Evaluate также перегружен, поэтому приведенный вами пример, скорее всего, вызывает метод с сигнатурой private decimal Evaluate(string expression, Dictionary<string, decimal> arguments).   -  person awh112    schedule 06.06.2018
comment
Нет-нет... Первый шаг: public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) ... Вау!   -  person xanatos    schedule 06.06.2018
comment
Ссылка, которую вы даете, является ссылкой на исходный код, почему бы не прочитать ее и не узнать, как она работает?   -  person Neil    schedule 06.06.2018
comment
@Neil Потому что я тоже точно не знаю, как это работает, и я неплохо разбираюсь в C #? Вероятно, это первый пример создания подкласса DynamicObject просто для того, чтобы проделать этот трюк.   -  person xanatos    schedule 06.06.2018
comment
Не могу решить, является ли эта библиотека злом, гениальным или злым гением...   -  person DavidG    schedule 06.06.2018
comment
@xanatos Сейчас он открыт, вы можете опубликовать. Возможно, стоит упомянуть, что engine также необходимо объявить как dynamic   -  person DavidG    schedule 06.06.2018
comment
Этот вопрос не должен быть закрыт, особенно с редактированием. ОП явно спрашивает, как компилятор может разрешать перегрузки, которые, по-видимому, не объявлены в связанном коде... Ксанатос ответил, как в комментариях.   -  person InBetween    schedule 06.06.2018
comment
@ Нил Я прочитал код ... и в основном понимаю, что он делает. Я просто не знаю, как создать объект, который бы соответствовал подписи.   -  person Greg    schedule 06.06.2018


Ответы (1)


В этой библиотеке используется магия высокого уровня... Очень высокий уровень :-)

Хитрость в том, что класс объявлен как:

public class ExpressionEvaluator : DynamicObject

Так что это класс, который реализует магию dynamic, представленную в .NET 4.0.

Теперь... В классе есть два метода 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> для передачи в private Evaluate().

Если вы попытаетесь использовать другую нотацию, например:

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

тогда .NET не может сопоставить метод с присутствующим public Evaluate(), но класс, являющийся подклассом DynamicObject, заставляет компилятор C# написать некий «магический» код, который запускает этот метод (который по-прежнему реализуется 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.

Теперь... Поскольку библиотека написана, вы можете:

  • #P15# <блочная цитата> #P16# #P17#
  • Используйте неанонимный объект (new Foo { a = 1, b = 2 c = 3 }). Библиотека не делает различий между анонимными и неанонимными объектами. То же ограничение, что и раньше, потому что во время компиляции вам нужен класс Foo с правильным количеством параметров.

  • Используйте обозначение dynamic. К сожалению, даже это довольно статично. Вы не можете легко добавить новые параметры, которые для количества и имени «переменных» должны быть определены во время компиляции.

Возможное решение состоит в том, чтобы изменить исходный код (это один файл) и сделать public этот метод:

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

то вы можете легко и динамично заполнить Dictionary<string, decimal> arguments

person xanatos    schedule 06.06.2018
comment
Поэтому я считаю (чтобы использовать ту же подпись), мне нужно будет динамически создать одну из этих new { a = 1, b = 2, c = 3 } Не знаю, как это сделать, но это отправная точка. Спасибо - person Greg; 06.06.2018
comment
@ Грег Да. Но обратите внимание, что количество и имя переменных будут фиксированными. К сожалению, библиотека не предоставляет private Evaluate, который использует метод Dictionary<>. Это было бы очень легко по-настоящему динамически использовать. - person xanatos; 06.06.2018
comment
Я согласен, и я клонировал его библиотеку и отправлю запрос на включение. Но я хотел убедиться, что нет способа использовать подпись как есть, прежде чем я начал запрашивать дополнительные функции. Спасибо еще раз. - person Greg; 06.06.2018
comment
@ Грег Я считаю, что самый простой способ - изменить private на public и использовать этот метод. Потому что создание анонимных объектов во время выполнения довольно сложно. - person xanatos; 06.06.2018