Laden...

Best practices - Task als Return-Value ohne Async?

Erstellt von Sebastian1989101 vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.923 Views
Sebastian1989101 Themenstarter:in
241 Beiträge seit 2010
vor 6 Jahren
Best practices - Task als Return-Value ohne Async?

Obwohl async / await nun schon eine ganze Weile existieren, bin ich jetzt das erste mal an einen Punkt angekommen, wo ich mir nicht sicher bin, wie es am besten implementiert wird. Und zwar folgendes:

Ich habe durch ein Framework, ein override, welches einen Task als Rückgabetypen hat. Nun benötige ich keinerlei asynchrones Verhalten in dieser Methode, da ich mir den Parameter nur in ein Field speichern möchte.

Aber was gebe ich nun in der Methode zurück? Ein Task.Delay(0)? Sieht mir nicht sonderlich elegant aus. Was macht ihr in solchen Fällen? Leider konnte ich bei Google auch nicht wirklich eine Antwort dazu finden. Es gibt zwar ein paar Ansätze dort, aber so elegant sind diese alle nicht.

WAGO Kontakttechnik GmbH & Co. KG / Software Notion
Softwareentwicklung

C# .NET with WPF, ASP, Xamarin and Unity
Personal Blog: Development Blog

H
114 Beiträge seit 2007
vor 6 Jahren

Hallo Sebastian1989101,

meine Empfehlung in diesem Fall wäre Task.CompletedTask

Grüße, HiGHteK

T
2.219 Beiträge seit 2008
vor 6 Jahren

Nimm den Task und rufe Wait() auf.
Dann kannst du aud Task.Result das Ergebnis liefern.

Habe einen ähnlichen Fall in einem Projekt bei uns.
Dort bekomme ich nur Async Methode obwohl ich auf das Ergebnis warten muss.
Also nehme ich den Task entgegen, warte auf diesen und liefere dann aus dem Result des Tasks das eigentliche Ergebnis bzw. wandle dieses dann um.

@HiGHteK
Für mich wird aus der Doku nicht ganz ersichtlich, in wie weit CompletedTask hier der richtige Ansatz wäre.
Da dies eine static Instanz ist, kann man gar nicht wissen welcher Task dies ist.
Kann auch ein völlig andere Task sein, wie der Hinweis am Ende auch hindeutet:
Repeated attempts to retrieve this property value may not always return the same instance.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

D
985 Beiträge seit 2014
vor 6 Jahren

@T-Virus

Der TE will eine Methode überschreiben (override).

Ich habe durch ein Framework, ein override, welches einen Task als Rückgabetypen hat. ...
Aber was gebe ich nun in der Methode zurück?

Also hat er eine Basis-Klasse die ungefähr so aussieht:


abstract class Foo
{
    public abstract Task DoBarAsync( object parameter )
}

In der abgeleiteten Klasse ist aber nichts was wirklich asynchron erfolgt. Also hat er keinen Task, den er zurückgeben kann.


class ConcreteFoo : Foo
{
    object _bar_parameter;

    public override Task DoBarAsync( object parameter )
    {
        _bar_parameter = parameter;
        return ... // what???
    }
}

Hier ist ein Task.CompletedTask genau das, was man dort zurückgeben möchte.

Diese statische Eigenschaft gibt es allerdings erst ab .NET 4.6. In einigen Libraries sieht man dort z.B. als Rückgabewert ein Task.FromResult( false ) was eben auch ein abgeschlossener Task ist.

Sebastian1989101 Themenstarter:in
241 Beiträge seit 2010
vor 6 Jahren

Hier ist ein Task.CompletedTask genau das, was man dort zurückgeben möchte.

Diese statische Eigenschaft gibt es allerdings erst ab .NET 4.6. In einigen Libraries sieht man dort z.B. als Rückgabewert ein Task.FromResult( false ) was eben auch ein abgeschlossener Task ist.

Das Projekt ist leider ein .NET 4.5 Projekt und somit ohne CompletedTask. Den werde ich wohl bei einen Task bleiben müssen, der ohnehin direkt abgeschlossen ist. Also Task.Delay(0), Task.FromResult(XY) oder was auch immer.

WAGO Kontakttechnik GmbH & Co. KG / Software Notion
Softwareentwicklung

C# .NET with WPF, ASP, Xamarin and Unity
Personal Blog: Development Blog

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo T-Virus,

Nimm den Task und rufe Wait() auf.
Dann kannst du aud Task.Result das Ergebnis liefern. N E I N ! ! ! Das blockiert den Thread ist sicher nicht das gewünschte Verhalten.

Hallo Sebastian1989101,

Also Task.Delay(0), Task.FromResult(XY) oder was auch immer.

Ist beides nicht ideal, da immer ein Task-Objekt erstellt werden muss und dieses vom GC dann abgeräumt werden muss. Task.Delay ist dazu noch schlechter -- kannst du selber nachlesen.

Erzeuge besser einmalig pro AppDomain einen abgeschlossenen Task und cache diesen, damit dieser verwendet werden kann. Task.CompletedTask macht nichts anderes. Also:


private static Task _completedTask = Task.FromResult(42);

Aber die Lösung zur Frage, sofern ich sie richtig verstanden habe, lässt sich einfacher und eleganter umsetzen (anhand des Beispiels von Sir Rufo):


class ConcreteFoo : Foo
{
    object _bar_parameter;

    public override Task DoBarAsync( object parameter )
    {
        _bar_parameter = parameter;
        return base.DoBarAsync(parameter);
    }
}

😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

D
985 Beiträge seit 2014
vor 6 Jahren

Was für einen konkreten Task liefert das denn zurück, wenn die Methode in der Basis-Klasse abstract deklariert ist?

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Sir Rufo,

aahh, ich hab dein abstract übersehen (weils in der Frage oben auch nicht steht), dann geht das natürlich nicht. Solle es aber eine nicht-abstrakte Basisklasse sein, so wäre dies der bevorzugte Weg.

Danke für den Hinweis!

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

Sebastian1989101 Themenstarter:in
241 Beiträge seit 2010
vor 6 Jahren

Ist leider abstract. 😉 Handelt sich um genau zu sein um die Initialize-Methode der MvvmCross ViewModel.

WAGO Kontakttechnik GmbH & Co. KG / Software Notion
Softwareentwicklung

C# .NET with WPF, ASP, Xamarin and Unity
Personal Blog: Development Blog

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Sebastian1989101,

der Sinn hinter dem Ganzen kommt mir ein wenig komisch vor. Was hast du mit diesem Konstrukt vor?* eine Basisklasse für deine ViewModels

  • Logging
  • ...

Je nachdem gibt es u.U. schönere Abbildungen davon. Z.B.* wenns die Basisklasse sein sollte: so können die Kindklassen diesen Parameter ja an base (deine Klasse) übergeben

  • ein Dekorator kann auch passen
  • ...

Ich weiß aber nicht was es werden soll, daher hänge ich da noch in der Luft.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

16.807 Beiträge seit 2008
vor 6 Jahren

Einfache Sache - eigentlich:

Zwei potentielle Varianten für die synchrone Umsetzung:


public Task Foo()
{
   return Task.CompletedTask;
}

public Task<T> Foo()
{
   T obj = irgendwoher synchron das T
    return Task.FromResult(obj);
}

Für vor .NET 4.6


private static Task completedTask = Task.FromResult(false);
public static Task CompletedTask()
{
    return completedTask;
}

Mehr oder weniger nichts anderes ist auch der originale Source dazu.

Hab aber auch schon das hier gesehen:


public static Task CompletedTask()
{
    return new Task(() => { });
}

Bei FromResult würde man über ein TaskCompletionSource gehen, sofern man auf .NET 4.0 (und nicht höher) angewiesen ist.

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Abt,

soweit waren wir weiter oben schon 😉
Und es ist .net 4.5, da gibt es CompletedTask nicht.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

16.807 Beiträge seit 2008
vor 6 Jahren

Lass mich zuende schreiben gfoidl 😛

D
985 Beiträge seit 2014
vor 6 Jahren

der Sinn hinter dem Ganzen kommt mir ein wenig komisch vor. Was hast du mit diesem Konstrukt vor?

Er leitet eine Klasse von einer Basis-Klasse aus einem externen Framework ab und muss nun diese in der Basis-Klasse als abstrakt definierte Methode Initalize implementieren?

Man könnte also sagen, der Sinn und Zweck ist es, dieses externe Framework zu verwenden.

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Sir Rufo,

ach wirklich? Darauf bin ich nicht gekommen 🤔

Ich hab ja auch konkrete Punkte angeführt was es sein könnte (Logging, eigene Basisklasse, ...) und will hinterfragen was er vorhat, denn ich glaube dass es eine besser Lösung gibt. Aber wenns nur um die Verwendung des externen Frameworks geht, so reich das natürlich 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

D
985 Beiträge seit 2014
vor 6 Jahren

Er will ganz einfach eine ViewModel-Klasse erstellen.

Initialize-Methode der MvvmCross ViewModel

Und konkret geht es um die Implementierung von IMvxViewModel bzw. die Ableitung von MvxViewModel (die Methode sieht allerdings auch nicht abstract aus).

Die konkreten Punkte habe ich gelesen, allerdings weiß ich nicht, wie man die hier in einen sinnvollen Kontext sehen kann oder in irgendeiner Art hilfreich sein sollten.

16.807 Beiträge seit 2008
vor 6 Jahren

Bin mir über die Verwendung von Init in MvvmCross nicht sicher; sieht für mich aber nach einer Bad Practise aus.
Init-Methoden werden oft aus dem Konstruktor aufgerufen und daher können diese - stand heute - nicht asynchron ausgeführt werden.
Init an für sich sollte also gar nicht async sein.

Damit muss man jetzt aber leben.
Die potentiellen Lösungsmöglichkeiten wurde doch aber nun genannt...?!

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo,

hier wird dann Initialiaze von der Init-Methode aufgerufen und Initialize ist virtual => return base.Initialize(...) ist dann die Wahl (siehe oben).

die hier in einen sinnvollen Kontext sehen kann oder in irgendeiner Art hilfreich sein sollten.

Daher wäre es interessant was der OT dazu meint, der wird wohl wissen was er damit vorhat. Wir können nur raten.
Wenns um Logging geht, so ist oft ein Dekorator passender als eine (Zwischen-) Ableitung. Wenns ein weitere eigene Basisklasse werden soll, so ist das Vorgehen OK. Aber ich weiß nicht was es werden soll, da es noch nirgends steht.

Aber Abt hat recht, die Lösungen stehen schon ganz oben und das hier ist eher abweichend vom eigentlichen Thema -- das können wir aber abteilen wenn wir wissen worum es geht und solange der OT keine Antwort schreibt, macht es wenig Sinn hier weiter zu raten.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

J
641 Beiträge seit 2007
vor 6 Jahren

Darf ich den einen Task den Ich mit FromResult erzeuge denn mehrmals verwenden? Microsoft macht ja da intern was anderes, und setzt noch das er nicht Disposed werden darf!

Siehe: (TaskCreationOptions)InternalTaskOptions.DoNotDispose

cSharp Projekte : https://github.com/jogibear9988

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo jogibear9988,

ja den kannst du ruhig öfters verwenden, warum auch nicht? Ist ja letztlich nur ein Objekt und somit auch nicht anders zu behandeln als jedes andere Objekt in .net.

Wenn -- wie oben erwähnt -- der Task in einem statischen Feld gehalten wird, so räumt den der GC auch nicht weg, da die Referenz auf diesen eben so lange lebt wie die AppDomain, also i.d.R. bis zum Ende des Prozesses.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"