Краткий метод передачи ассоциативных массивов в метод
-
22-08-2019 - |
Вопрос
Ищем способ передать ассоциативный массив методу.Я хочу переписать пакет анимации Actionscript на C#, но у меня возникли проблемы с «ассоциативными» массивами/объектами.Обычно в Actionscript я могу сделать что-то вроде:
public function tween(obj:DisplayObject, params:Object = null):void {
if (params.ease != null) {
//do something
}
//...
}
И это можно было бы назвать так:
tween(this, {ease:'out', duration:15});
Я ищу способ сделать то же самое на С#.На данный момент я собрал следующие варианты:
а) создание класса или структуры для определения возможных ключей и типов параметров и их передачи
б) передать параметры как общий тип
tween(frameworkElement, new {ease = 'out', duration = 15});
предполагая
public static void tween(FrameworkElement target, object parameters);
и найдите способ использовать это в функции анимации (я не уверен, как разделить ключ = значение для этого объекта.Любые идеи?)
в) создать Dictionary<string, object>
передать параметры в функцию анимации
Есть еще идеи или пример кода?Я новичок в C#.
Редактировать
Мне потребовался целый день, чтобы понять это:
«Анонимные типы не могут использоваться совместно за пределами сборок.Компилятор гарантирует, что в каждой сборке существует не более одного анонимного типа для заданной последовательности пар имя/тип свойства.Чтобы передавать структуры между сборками, вам необходимо правильно их определить».
Решение
РЕДАКТИРОВАТЬ:По сути, это механизм, который расширения HtmlHelper используют в ASP.NET MVC.У меня это не оригинально.
Я бы предпочел гибридный подход с двумя разными сигнатурами.Примечание:Я не пробовал этого, и между двумя сигнатурами может возникнуть конфликт, поэтому вам, возможно, придется дать второму методу немного другое имя, чтобы компилятор мог выбирать между ними, но я так не думаю.
public static void tween( FrameworkElement target, object parameters )
{
return tween( target, new ParameterDictionary( parameters ) );
}
public static void tween( FrameworkElement target,
ParameterDictionary values )
{
if (values.ContainsKey( "ease" ))
{
....
}
}
Затем у вас есть класс ParameterDictionary, который использует отражение анонимного типа и настраивает словарь.
public class ParameterDictionary : Dictionary<string,object>
{
public ParameterDictionary( object parameters )
{
if (parameters != null)
{
foreach (PropertyInfo info in parameters.GetType()
.GetProperties())
{
object value = info.GetValue(parameters,null);
this.Add(info.Name,value);
}
}
}
}
Это дает вам как простоту использования, так и простоту потребления — «уродливые» отражения заключаются в единственный конструктор словаря, а не в ваш метод.И, конечно же, словарь можно использовать снова и снова для аналогичных целей, при этом код отражения пишется только один раз.
Другие советы
Словарь представляет собой разумный подход.Это может выглядеть так:
public static void tween(FrameworkElement target, IDictionary<string, object> parameters)
{
if (parameters.ContainsKey("ease")) {
//do something
}
}
Имейте в виду, что при передаче значений вы можете использовать сокращенный синтаксис инициализации коллекции, чтобы упростить задачу, например:
tween(something, new Dictionary<string, object> { { "ease", "out" }, { duration, 15 } });
Это был бы довольно быстрый способ сделать примерно «то же самое», что и код ActionScript, но на C# он не очень хорош.Способ сделать это в стиле C# — создать настоящую структуру или класс TweenParameters со свойствами для каждого возможного параметра, а затем создать такую структуру для передачи в tween().Обычно это считается более удобным в обслуживании, поскольку все «доступные» свойства очевидны для вызывающего объекта без изучения внутреннего устройства tween(), а также потому, что он улавливает ошибки во время компиляции, которые при сравнении строк не будут заметны до времени выполнения, например набираю "длительность" "дюратоин".
Я сам являюсь поклонником варианта (b) — передачи анонимного типа и анализа значений с использованием отражения.
Я видел, как другие добивались того же самого, используя лямбда-выражения.Синтаксис вызова будет следующим:
tween(frameworkElement, ease => "out", duration => 15);
И декларация будет примерно такой:
public static void tween(FrameworkElement target, params Expression<Func<object>>[] parameters) { ... }
Идея состоит в том, что вы можете взять переменное количество «функций, возвращающих объект».Затем вы анализируете имя параметра из каждого Выражение<TDelegate>, и вызовите каждый из них, чтобы получить его значение.
Я не думаю, что это лучше, чем размышления над анонимным типом, но это еще один подход, который стоит рассмотреть.
Обновлять
Я уже писал об идее передачи ассоциативных массивов в виде словарей в своем блоге. здесь и здесь.