C# 滥用关闭?违反各种原则?还是好?

C# 滥用关闭?违反各种原则?还是好?,c#,closures,anti-patterns,principles,command-query-separation,C#,Closures,Anti Patterns,Principles,Command Query Separation,编辑:修复了几个语法和一致性问题,使代码更清晰,更接近我实际正在做的事情 我有一些代码如下所示: SomeClass someClass; var finalResult = DoSomething(() => { var result = SomeThingHappensHere(); someClass = result.Data; return result; }) .DoSomething(() => return SomeOthe

编辑:修复了几个语法和一致性问题,使代码更清晰,更接近我实际正在做的事情

我有一些代码如下所示:

SomeClass someClass;
var finalResult = 
  DoSomething(() => 
  {
    var result = SomeThingHappensHere();
    someClass = result.Data;
    return result;
  })
  .DoSomething(() => return SomeOtherThingHappensHere(someClass))
  .DoSomething(() => return AndYetAnotherThing())
  .DoSomething(() => return AndOneMoreThing(someClass))
  .Result;

HandleTheFinalResultHere(finalResult);
其中,
DoSomething
方法是一个扩展方法,它需要传入一个Func。因此,每个DoSomething=>lambda中的每个方法调用都返回一个结果类型

这类似于一个。除了检查null之外,我正在检查Result类的状态,或者调用传递到DoSomething中的Func,或者在不调用Func的情况下返回上一个结果

我面临的问题是,我希望在代码中有这种组合,但我还需要能够将数据从一个组合的调用结果传递到另一个的调用,正如您通过
someClass
变量所看到的那样


我的问题不是这在技术上是否正确。。。我知道这很有效,因为我现在正在做。我的问题是这是否是对闭包、命令查询分离或任何其他原则的滥用。。。然后问有什么更好的模式可以处理这种情况,因为我相当肯定,我现在被困在这种类型的代码的“闪亮的新锤子”模式中。

在我看来,你在这里构建了非常类似于单子的东西


您可以通过使您的委托键入a
Func
使其成为适当的monad,并通过某种方式设置要传入的初始
SomeClass
值,并让DoSomething将其中一个的返回值作为下一个的参数传递——这将使链接显式化,而不是依赖于词汇范围的共享状态。

此代码的弱点是第一个和第二个lambda之间的隐式耦合。我不确定最好的解决办法

如前所述,这里几乎实现了一个Monad

你的代码有点不雅观,因为lambda有副作用。单子更优雅地解决了这个问题

那么,为什么不把你的代码转换成一个合适的单子呢?

奖励:您可以使用LINQ语法


我提出:

LINQ对结果的影响
例如:

使用LINQ to Results,首先执行
此处发生的某些事情
。如果成功,它将获取结果的
Data
属性的值,并执行
SomeOtherThingHappensHere
。如果成功,它将执行
和yetanotherthing
,依此类推

如您所见,您可以轻松地链接操作并参考以前操作的结果。每个操作将一个接一个地执行,当遇到错误时,执行将停止

每行x中的
位都有点嘈杂,但在我看来,没有比这更复杂的了


我们如何使这项工作顺利进行?

C#中的单子由三部分组成:

  • 一种T型的东西

  • 选择
    /
    为它选择许多扩展方法,然后

  • 一种将T转换为T中某物的方法

你所需要做的就是创造一些看起来像单子,感觉像单子,闻起来像单子的东西,一切都会自动工作


LINQ to结果的类型和方法如下所示

结果类型:

表示结果的简单类。结果要么是类型为T的值,要么是错误。可以从T或异常构造结果


例如,您可以自由修改结果类和扩展方法,以实现更复杂的规则。只有扩展方法的签名必须完全符合规定。

这不太容易阅读……100%同意。可悲的是,与我开始时相比,这仍然是一个显著的进步。再加一点格式可能会有帮助。。。可能…所以本质上你是在尝试进行一系列独立的方法调用,并将每个调用的结果组合成一个结果。对吗?或者你到底想达到什么目的?dtb:这类似于单子。除了检查null之外,我正在检查Result类的状态,或者调用传递到DoSomething中的Func,或者在不调用funcy的情况下返回上一个结果。对于单行lambda,您不需要return关键字。我只是说,是的,我最近一直在学习单子,我试图按照这些思路创造一些东西。您的建议的问题是,我需要始终从函数返回“result”类,在一种情况下,我还需要将“someClass”数据从一个函数返回到下一个函数。+1副作用很难推理和测试。如果您重构为
Func
,而不是
Action
,并显式地链接操作,那么您会感谢您自己。为什么让结果类型如此重要?它是通过其他人的代码传递的吗?在返回之前,您不能修改它,以便在合成链中附加另一个链接?DoSomething方法中有几个检查和逻辑片段,使用结果类型来确定是否调用传入的Func以获得下一个结果,或者只返回当前的result.Waitaminute。如果Result有一个SomeClass类型的成员(我猜它是某种条件参数块),为什么不使用该成员将其传递到lambda链,而不是创建一个对所有lambda都可见的单独可变变量呢?当然。这是糟糕的设计和语义耦合——你必须知道引擎盖下发生了什么,才能理解将价值从其中一个拉到另一个的后果。太棒了。有一段时间我一直在迷惑蒙娜斯,这很有道理,肯定有助于我的理解。您不能将Select/SelectMany扩展中的大部分逻辑移到Bind(Func)或si中吗
var result =
    from a in SomeThingHappensHere()
    let someData = a.Data
    from b in SomeOtherThingHappensHere(someData)
    from c in AndYetAnotherThing()
    from d in AndOneMoreThing(someData)
    select d;

HandleTheFinalResultHere(result.Value);
class Result<T>
{
    private readonly Exception error;
    private readonly T value;

    public Result(Exception error)
    {
        if (error == null) throw new ArgumentNullException("error");
        this.error = error;
    }

    public Result(T value) { this.value = value; }

    public Exception Error
    {
        get { return this.error; }
    }

    public bool IsError
    {
        get { return this.error != null; }
    }

    public T Value
    {
        get
        {
            if (this.error != null) throw this.error;
            return this.value;
        }
    }
}
static class ResultExtensions
{
    public static Result<TResult> Select<TSource, TResult>(this Result<TSource> source, Func<TSource, TResult> selector)
    {
        if (source.IsError) return new Result<TResult>(source.Error);
        return new Result<TResult>(selector(source.Value));
    }

    public static Result<TResult> SelectMany<TSource, TResult>(this Result<TSource> source, Func<TSource, Result<TResult>> selector)
    {
        if (source.IsError) return new Result<TResult>(source.Error);
        return selector(source.Value);
    }

    public static Result<TResult> SelectMany<TSource, TIntermediate, TResult>(this Result<TSource> source, Func<TSource, Result<TIntermediate>> intermediateSelector, Func<TSource, TIntermediate, TResult> resultSelector)
    {
        if (source.IsError) return new Result<TResult>(source.Error);
        var intermediate = intermediateSelector(source.Value);
        if (intermediate.IsError) return new Result<TResult>(intermediate.Error);
        return new Result<TResult>(resultSelector(source.Value, intermediate.Value));
    }
}