Frage

Edit:. Mehrere Syntax und Konsistenz Probleme behoben den Code ein wenig deutlicher und in der Nähe zu machen, was ich eigentlich tue

Ich habe einige Code bekam das wie folgt aussieht:

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

, wo die DoSomething Methode ist eine Erweiterung Methode und erwartet ein Func hineingeben. So kann jeder der Methodenaufrufe in jedem der DoSomething => LAMBDA liefert einen Ergebnistyp.

Das ist ähnlich wie bei einem Vielleicht Monade . Außer stattdessen für nulls der Prüfung, ich bin Überprüfung des Status der Ergebnisklasse, und entweder die Func aufrufen, die das vorherige Ergebnis in DoSomething oder Rückkehr ohne Aufruf der Func übergeben wurde

das Problem, das ich Gesicht ist, dass wollen, dass diese Art der Zusammensetzung in meinem Code haben, aber ich muss auch in der Lage sein, Daten zu übergeben von einem der zusammengesetzt Anruf Ergebnisse in den Ruf eines anderen, wie Sie mit dem someClass sehen Variable.

Meine Frage ist nicht, ob dies technisch korrekt ist ... Ich weiß, das funktioniert, weil ich zur Zeit, es zu tun habe. Meine Frage ist, ob dies ist ein Missbrauch von Verschlüssen oder Befehl Abfrage Trennung oder anderer Prinzipien ... und dann zu fragen, was besser Muster gibt es diese Situation für die Handhabung, weil ich ziemlich sicher bin, dass ich in einem „glänzenden neuen Hammer“ -Modus mit dieser Art von Code stecken, gerade jetzt.

War es hilfreich?

Lösung

Wie bereits erwähnt, haben Sie fast ein Monad hier umgesetzt werden.

Der Code ist ein wenig unelegant, dass die Lambda-Ausdrücke Nebenwirkungen haben. Monaden löst dieses elegante.

Also, warum nicht drehen Sie den Code in eine richtige Monad?

Bonus: Sie LINQ-Syntax verwenden können,


I vorhanden:

LINQ to Ergebnisse


Beispiel:

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

Mit LINQ to Ergebnisse , dieser führt zuerst SomeThingHappensHere. Wenn das gelingt, wird es den Wert der Data Eigenschaft des Ergebnisses und führt SomeOtherThingHappensHere. Wenn das erfolgreich ist, führt er AndYetAnotherThing, und so weiter.

Wie Sie sehen können, können Sie einfach Chain-Operationen und Ergebnisse früherer Operationen beziehen. Jede Operation wird nacheinander durchgeführt werden, und die Ausführung wird gestoppt, wenn ein Fehler auftritt.

Die from x in Bit jede Zeile ist ein bisschen laut, aber IMO nichts vergleichbarer Komplexität erhalten besser lesbar als das!


Wie machen wir diese Arbeit?

Monaden in C # besteht aus drei Teilen:

  • eine Art Something-of-T

  • Select / SelectMany Erweiterungsmethoden für sie, und

  • ein Verfahren ein T in eine Something-of-T umwandeln .

Alles, was Sie tun müssen, ist etwas zu schaffen, das aussieht wie eine Monade, fühlt sich wie ein Monad und riecht wie eine Monade, und alles wird automatisch arbeiten.


Die Typen und Methoden für LINQ to Ergebnisse sind folgen wie.

Ergebnis Typ:

Eine einfache Klasse, die ein Ergebnis darstellt. Ein Ergebnis ist entweder ein Wert vom Typ T , oder ein Fehler. Ein Ergebnis kann aus einen konstruiert werden T oder von einer Ausnahme .

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

Erweiterungsmethoden:

Implementationen für die Select und SelectMany Methoden. Die Methode Signaturen werden in der C # spec gegeben, so alles, was Sie Angst haben über ihre Implementierungen ist. Diese kommen ganz natürlich, wenn Sie versuchen, alle Methodenargumente in einer sinnvollen Art und Weise zu kombinieren.

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

Sie können frei das Ergebnis Klasse und die Erweiterungsmethoden, zum Beispiel ändern, komplexere Regeln zu implementieren. Nur die Unterschriften der Erweiterungsmethoden genau wie angegeben werden müssen.

Andere Tipps

scheint mir, wie du etwas sehr ähnlich zu einem Monade hier gebaut haben.

Sie könnten es machen eine richtige Monade, indem Sie Ihre Delegierten einen Func<SomeClass, SomeClass> geben, haben in gewisser Weise die anfängliche SomeClass Wert einzurichten passieren in und haben DoSomething den Rückgabewert einer als Parameter des nächsten übergeben - das würde die Verkettungs explizit zu machen, anstatt auf lexikalischen gemeinsamen Staat zu verlassen.

Die Schwäche dieses Codes ist die implizite Kopplung zwischen der ersten und den zweiten Lambda-Ausdrücke. Ich bin mir nicht sicher, ob der beste Weg, um es zu beheben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top