Может быть, монада, использующая деревья выражений? - PullRequest
5 голосов
/ 10 декабря 2010

Гадкий:

string city = null;
if (myOrder != null && myOrder.Customer != null)
  city = myOrder.Customer.City;

Лучше ( возможно, монада ):

var city = myOrder
               .With(x => x.Customer)
               .With(x => x.City)

Еще лучше?По какой-либо причине это не может быть написано?

var city = Maybe(() => myOrder.Customer.City);

Ответы [ 5 ]

3 голосов
/ 10 декабря 2010

Да, это должно быть возможно. Однако это немного сложнее, чем кажется на первый взгляд, для правильной реализации переписывающего устройства дерева выражений. Особенно, если вы хотите иметь возможность правильно обрабатывать поля, свойства, индексированные свойства, вызовы методов и другие конструкции, допустимые в произвольных выражениях.

Это также может быть не самая эффективная операция, поскольку для оценки выражения необходимо динамически компилировать дерево выражений в лямбда-функцию каждый раз.

Существует реализация для этого шаблона в CodePlex . Я никогда не использовал его лично, поэтому я не могу сказать, насколько хорошо он реализован или обрабатывает ли он все описанные мной случаи.

Альтернативой созданию средства переписывания дерева выражений является запись Maybe() для принятия лямбда-функции (а не дерева выражений) и перехвата любого ArgumentNullException сгенерированного значения, возвращающего default(T) в этих случаях. Это приводит к тому, что многие люди неправильно используют исключения для управления потоком таким образом ... но это, безусловно, более простая реализация. Я лично избегаю этого, поскольку он может маскировать ошибки нулевых ссылок в методах, вызываемых как часть выражения, что нежелательно.

2 голосов
/ 02 августа 2011

Недавно я реализовал некоторые монады в C # (включая синтаксический анализатор базовых выражений, вдохновленный статьей Бартоша Милевского).

Посмотрите, если вам интересно: https://github.com/htoma/monads/blob/master/expressionMonad/expressionMonad/Program.cs

1 голос
/ 09 июля 2012

Некоторые моменты, которые приходят мне в голову:

  • .Решения отлично работают для объектов памяти, но сталкиваются с проблемами в EF, поскольку эти статические вызовы не могут быть преобразованы для работы с постоянным хранилищем (то есть SQL БД).Это несколько ограничивает область применения.

  • Я практически всегда хочу знать, действительно ли цепочка дала действительный результат.Следовательно, у меня будет один условный блок if(city == null) в любом случае.

  • Любое текущее решение, кроме "уродливого", включает выражения.

Следовательно, мой выбор будет выглядеть примерно так:

var property = ( () => myOrder.Customer.City );
city = HasValue(property) ? property.Invoke() : "unknown";

HasValue(Expression e) рекурсивно обходится по дереву выражений LINQ до тех пор, пока оно не достигнет конца (возвращает true) или не встретит свойство с нулевым значением (возвращает false).Реализация должна быть простой, используйте MethodInfo Member из MemberExpression класса для разбора AST.Можно также реализовать getter таким образом, как предложил Брайан, но мне больше нравится выше, потому что HasValue всегда возвращает bool.Далее:

  • Также можно обрабатывать вызовы членов.
  • Оценка может быть сделана как myOrder.HasValue(x => x.Customer.City), но это вызывает некоторые сложности.
0 голосов
/ 08 июля 2012

Я знаю, что моя реализация Maybe (в соответствии со статьей CodeProject) влечет за собой затраты, но я уверен, что это ничто по сравнению с идеей вовлечения Expression<T>.По сути, вы говорите «Отражение» полностью.Я бы не возражал, если бы он был предварительно скомпилирован в стиле Roslyn, но мы еще не пришли.

Я бы сказал, что преимущество моей реализации выходит далеко за рамки мифологии?оператор.Возможность написать целый алгоритм, используя цепочку, такую ​​как эта, означает, что вы можете внедрять свои собственные творения (например, If, Do и т. Д.) И предоставлять свою собственную специализированную логику.

Я понимаю,это сложнее, чем то, что вы пытаетесь сделать здесь, но не похоже, что мы получим нуль-сливающийся оператор точки в C # 5.

0 голосов
/ 10 декабря 2010

Более простой ответ, если объекты дешевы в создании, и вы хотите избежать проверок на нуль:

myOrder.NewIfNull().Customer.NewIfNull().City;

Это вернет либо нулевое, либо некоторое начальное значение, которое вы установили в конструкторе или инициализаторе поля для City. NewIfNull не встроен, но он очень прост:

public static T NewIfNull<T>(this T input) where T:new()
{
   return input ?? new T();
}
...