Цепочка для сложного взаимодействия последовательностей делегатов - PullRequest
2 голосов
/ 13 февраля 2011

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

Без дальнейших действий, Я объясню:

Сценарий состоит в том, что у вас есть делегаты перевода (Func [A, B]) и поведения перевода (Func [A, Func [Func [A, B], B]]).

Идея состоит в том, что вокруг данного перевода у вас будет определенный набор поведений, которые обертывают вызов к переводу, то есть - что они принимают A, не стесняйтесь делать с ним то, что они хотят,передать это следующему поведению, в то же время имея возможность изменить возвращаемое значение (B).

Вероятно, есть некоторая монада, чтобы описать это идеально, но, возможно, некоторый код поможет больше, чем что-либо.

Жертвы:

public class B
{

}

public class A
{

}

Делегат поведения

public delegate Func<Func<A, B>, B> TranslationBehavior(A input);

Функция, которая объединяет их в цепочку и возвращает Func, который позволяет перевестиИонная функция, которую нужно передать и получить новую функцию перевода, которая обернута поведением

static Func<Func<A, B>, Func<A, B>> Chain(IEnumerable<TranslationBehavior> behaviors)
{
    throw new NotImplementedException();
}

сценарий использования

static void Main(string[] args)
{
    var behaviors = new[]
    {
        (TranslationBehavior) (inp => next => next(inp)),
        (TranslationBehavior) (inp => next => next(inp)),
        (TranslationBehavior) (inp => next => next(inp)),
    };

    var input = new A();

    var chained = Chain(behaviors);

    var output = chained(a => new B());

}

В примере кода реализации поведения ничего не делаютput вызывает следующее поведение, и наша реализация перевода просто возвращает новый B.

Функция 'цепочка' является проблемной функцией, возможность связать поведение вместе ускользнула от меня, но чтобы доказать себе, что это должнона самом деле, я жестко запрограммировал наивное решение, которое специально объединяет три вида поведения:

static Func<Func<A, B>, Func<A, B>> Chain(IEnumerable<TranslationBehavior> behaviors)
{
    var behavior1 = (TranslationBehavior)null;
    var behavior2 = (TranslationBehavior)null;
    var behavior3 = (TranslationBehavior)null;

    return translation => input =>
        behavior1(input)(transformed1 =>
            behavior2(transformed1)(transformed2 =>
                behavior3(transformed2)(translation)
            )
        );
}

Это работает, но очевидно;совершенно бесполезно.

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

Заранее спасибо, Стивен.

Ответы [ 2 ]

3 голосов
/ 13 февраля 2011

Я не совсем понял ваш сценарий - без какого-либо дополнительного контекста он звучит слишком сложно :-) Однако, я думаю, что из-за типов, реализация, которую вы ищете, выглядит примерно так. Хитрость заключается в том, чтобы добавить метод, который работает с IEnumerator и является рекурсивным:

Func<Func<A, B>, Func<A, B>> Chain
  (IEnumerable<TranslationBehavior> behaviors, Func<A, B> final) {
    return translation => Chain(behaviors.GetEnumerator(), translation);
}

// Recursive method that takes IEnumerator and processes one element..
Func<A, B> Chain
  (IEnumerator<TranslationBehavior> behaviors, Func<A, B> last) {
    if (behaviors.MoveNext())
      return input => behaviors.Current(input)(Chain(behaviors, last));
    else
      return last;
}

Вы можете добавить обработку исключений и удалить перечислитель, но это должна быть правильная структура.

1 голос
/ 13 февраля 2011

Идея состоит в том, что вокруг данного перевода у вас будет определенный набор поведений, которые оборачивают вызов к переводу, то есть - что они принимают A, не стесняйтесь делать с ним то, что они хотят,передайте это следующему поведению, в то же время имея возможность изменить возвращаемое значение (B).

Это предложение кажется наиболее ясным изложением вашей проблемы в этом вопросе.Похоже, у вас есть

A -> {Black Box} -> B

и вы хотите

A -> pre-process A -> {same Black Box} -> B -> post-process and return B

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

interface ITranslator 
{
    B Transform(A input);
}
class TranslatorWrapper : ITranslator
{
    TranslatorWrapper(ITranslator wrapped)
    {
        _wrapped = wrapped;
    }

    ITranslator _wrapped;

    B Transform(A input)
    {
        //do preprocessing here
        B result = _wrapped.Transform(input);
        //do postprocessing here
        return result;
    }
}

Приятно то, что вы можете передавать "nest" или "chain" TranslatorWrappers и другие подобные классы с помощьюпередавая их в параметрах конструктора.В зависимости от синтаксиса, который вы хотите, вы можете добавить несколько методов расширения, чтобы обеспечить более гибкий стиль кодирования для этих оболочек.

...