链接委托序列的复杂交互

时间:2011-02-12 23:50:24

标签: c# delegates monads method-chaining

这个问题感觉非常复杂,虽然解决方案看起来很简单,但代表们委托代表的剪切思维令人难以置信地从更多的代表那里回来,这让我的大脑自然崩溃了。

如果没有进一步做,我会解释:

场景是你有翻译代表(Func [A,B])和翻译行为(Func [A,Func [Func [A,B],B]])。

这个想法是围绕一个给定的翻译,你会有一组特定的行为将调用包装到翻译中,即他们接受A,随意用它做什么,传递给它到下一个行为,同时能够改变返回值(B)。

可能有一些monad可以完美地描述这一点,但也许一些代码会比任何事情更有帮助。

受害者:

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());

}

在示例代码中,行为实现不执行任何操作,调用下一个行为,我们的转换实现只返回一个新的B.

函数'chain'是问题函数,能够将行为链接在一起我已经躲过了,但为了向自己证明这应该实际工作,我硬编码了一个特别将三个行为链接在一起的天真解决方案:

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)
            )
        );
}

这很有效,但很明显;没用。

任何有关这方面的帮助都非常有用,关于这是一个已知模式或monad的任何信息都会非常有趣,我不认为这段代码远非一般化。

提前致谢, 斯蒂芬。

2 个答案:

答案 0 :(得分:3)

我没有完全理解你的场景 - 没有一些额外的背景,它听起来太复杂了:-)但是,仅从类型来看,我认为你正在寻找的实现是这样的。诀窍是添加一个与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 :(得分:1)

  

这个想法是围绕一个给定的翻译,你会有一组特定的行为将调用包装到翻译中,即他们接受A,随意用它做他们想要的,传递这个到下一个行为,同时能够改变返回值(B)。

这句话似乎是你问题中最清楚的问题陈述。听起来像你有

A -> {Black Box} -> B

并希望

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

如果这就是你想要的,我会推荐一个接口和一个包含另一个实现的接口的实现。这似乎与复合或代理模式类似,但它不是一个。

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和其他类似的类。根据您需要的语法,您可能需要添加一些扩展方法,以便为这些包装器提供更流畅的编码样式。