I'm trying create a class that has events AND can be awaitable, but keep coming across stumbling blocks.
First, I tried a TransferJob class that returns a TransferTask object which is already running when it is returned. This would be accomplished through something like this:
public abstract class TransferJob
{
public TransferTask Start()
{
return Start(CancellationToken.None);
}
public TransferTask Start(CancellationToken token)
{
TransferTask task = CreateTransferTask();
task.Start(token);
return task;
}
protected abstract TransferTask CreateTransferTask();
}
public abstract class TransferTask
{
public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;
private Task transferTask;
private TransferStatus status;
public TransferStatus Status
{
get { return this.status; }
protected set
{
TransferStatus oldStatus = this.status;
this.status = value;
OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
}
}
internal void Start(CancellationToken token)
{
this.transferTask = TransferAsync(cancellationToken);
}
protected abstract Task TransferAsync(CancellationToken cancellationToken);
protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
{
if (this.StatusChanged != null)
{
this.StatusChanged(this, txStatusArgs);
}
}
public TaskAwaiter GetAwaiter()
{
return this.transferTask.GetAwaiter();
}
}
The problem with the above is that if the TransferTask finishes very quickly, then users of TransferJob.Start() might not have time to register their event handlers on the returned TransferTask's StatusChanged event before it finishes. So I tried a different approach whereby the user has to call the TransferTask's Start() method themselves. This would give the user time to register their event handlers on the TransferTask in between the transferJob.CreateTask() call and the transferTask.Start() call:
public abstract class TransferJob
{
public abstract TransferTask CreateTask();
}
public abstract class TransferTask
{
public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;
private Task transferTask;
private TransferStatus status;
public TransferStatus Status
{
get { return this.status; }
protected set
{
TransferStatus oldStatus = this.status;
this.status = value;
OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
}
}
public void Start(CancellationToken token)
{
this.transferTask = TransferAsync(cancellationToken);
}
protected abstract Task TransferAsync(CancellationToken cancellationToken);
protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
{
if (this.StatusChanged != null)
{
this.StatusChanged(this, txStatusArgs);
}
}
public TaskAwaiter GetAwaiter()
{
return this.transferTask.GetAwaiter();
}
}
Now, I have a different problem. If a user tries await transferTask; before transferTask.Start(); has been called, then presumably they'll get a NullReferenceException thrown because the task hasn't been started (and therefore assigned to the transferTask field). I'm really struggling for a way to solve this. Is there a way? Or a better pattern to use than the above?
I'm not really convinced this is a good idea. Just expose the TAP pattern. Delete the event as well as transferTask. The caller of Start must hold onto that task and pass it to any code that wants to listen for completion. This results in a very clean API. No mutable state, very simple to understand, supports all use cases.
If you insist, you can create a proxy task that looks like it's the real thing:
public abstract class TransferTask
{
public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;
private TaskCompletionSource<object> transferTask = new ...; //changed
private TransferStatus status;
public TransferStatus Status
{
get { return this.status; }
protected set
{
TransferStatus oldStatus = this.status;
this.status = value;
OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
}
}
public Task Start(CancellationToken token)
{
await TransferAsync(cancellationToken);
transferTask.SetResult(null); //complete proxy task
}
protected abstract Task TransferAsync(CancellationToken cancellationToken);
protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
{
if (this.StatusChanged != null)
{
this.StatusChanged(this, txStatusArgs);
}
}
public TaskAwaiter GetAwaiter()
{
return this.transferTask.Task.GetAwaiter(); //changed
}
}
Now, transferTask.Task is always not null. That task will eventually complete. I quickly hacked this together, I hope the idea is clear.
Probably, you should base the event on transferTask.Task.ContinueWith(...).
The best way I found when trying to mix events and awaitable code in C# is to use the Reactive Extension (Rx) library. From Microsoft:
Reactive Extension (Rx) is a library to compose asynchronous and event-based programs using observable collections and LINQ-style query operators.
You could do something like the following to fix your issue. (I am not sure this is exactly what you want to accomplish, but the goal is just to demonstrate how Rx can be used to combine events with asynchronous code):
public async Task TransferAndWaitStartedAsync()
{
var transferTask = new TransferTask();
// Prepare the observable before executing the transfer to make sure that the observable sequence will receive the event
// You can use Linq operators to filter only specific events. In this case, I only care about events with Status == StatusCode.Started
var whenStatusChanged = Observable.FromEventPattern<TransferStatusChangedEventArgs>(h, transferTask.StatusChanged += h, h => transferTask.StatusChanged -= h)
.Where(e => e.EventArgs.Status == StatusCode.Started)
.FirstAsync();
// Start the transfer asynchronously
await transferTask.TransferAsync();
// Continuation will complete when receiving the first event that matches the predicate in the observable sequence even if the event was triggered too quickly.
await whenStatusChanged;
}
I find that the Rx library has a steep learning curve with all its subtleties, but when you know how to use it, it is a really powerful tool.
Intro to Rx with lot of examples
Design guidelines
Related
I working on real-time search. At this moment on property setter which is bounded to edit text, I call a method which calls API and then fills the list with the result it looks like this:
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
RunInAsync(LoadData);
}
}
private async Task LoadData()
{
var response = await connectionRepository.GetConnections(currentPage,
pageSize, searchPhrase);
foreach (UserConnection uc in response)
{
if (uc.Type != UserConnection.TypeEnum.Awaiting)
{
RunOnMainThread(() =>
SearchResult.Add(new ConnectionUser(uc)));
}
}
}
But this way is totally useless because of it totally mashup list of a result if a text is entering quickly. So to prevent this I want to run this method async in a property but if a property is changed again I want to kill the previous Task and star it again. How can I achieve this?
Some informations from this thread:
create a CancellationTokenSource
var ctc = new CancellationTokenSource();
create a method doing the async work
private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
// more code here
// check again if this task is canceled
token.ThrowIfCancellationRequested();
// more code
}
}
It is important to have this checks for cancel in the code.
Execute the function:
var cancellable = ExecuteLongCancellableMethod(ctc.Token);
To stop the long running execution use
ctc.Cancel();
For further details please consult the linked thread.
This question can be answered in many different ways. However IMO I would look at creating a class that
Delays itself automatically for X (ms) before performing the seach
Has the ability to be cancelled at any time as the search request changes.
Realistically this will change your code design, and should encapsulate the logic for both 1 & 2 in a separate class.
My initial thoughts are (and none of this is tested and mostly pseudo code).
class ConnectionSearch
{
public ConnectionSearch(string phrase, Action<object> addAction)
{
_searchPhrase = phrase;
_addAction = addAction;
_cancelSource = new CancellationTokenSource();
}
readonly string _searchPhrase = null;
readonly Action<object> _addAction;
readonly CancellationTokenSource _cancelSource;
public void Cancel()
{
_cancelSource?.Cancel();
}
public async void PerformSearch()
{
await Task.Delay(300); //await 300ms between keystrokes
if (_cancelSource.IsCancellationRequested)
return;
//continue your code keep checking for
//loop your dataset
//call _addAction?.Invoke(uc);
}
}
This is basic, really just encapsulates the logic for both points 1 & 2, you will need to adapt the code to do the search.
Next you could change your property to cancel a previous running instance, and then start another instance immediatly after something like below.
ConnectionSearch connectionSearch;
string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
//do your setter work
if(connectionSearch != null)
{
connectionSearch.Cancel();
}
connectionSearch = new ConnectionSearch(value, addConnectionUser);
connectionSearch.PerformSearch();
}
}
void addConnectionUser(object uc)
{
//pperform your add logic..
}
The code is pretty straight forward, however you will see in the setter is simply cancelling an existing request and then creating a new request. You could put some disposal cleanup logic in place but this should get you started.
You can implement some sort of debouncer which will encapsulate the logics of task result debouncing, i.e. it will assure if you run many tasks, then only the latest task result will be used:
public class TaskDebouncer<TResult>
{
public delegate void TaskDebouncerHandler(TResult result, object sender);
public event TaskDebouncerHandler OnCompleted;
public event TaskDebouncerHandler OnDebounced;
private Task _lastTask;
private object _lock = new object();
public void Run(Task<TResult> task)
{
lock (_lock)
{
_lastTask = task;
}
task.ContinueWith(t =>
{
if (t.IsFaulted)
throw t.Exception;
lock (_lock)
{
if (_lastTask == task)
{
OnCompleted?.Invoke(t.Result, this);
}
else
{
OnDebounced?.Invoke(t.Result, this);
}
}
});
}
public async Task WaitLast()
{
await _lastTask;
}
}
Then, you can just do:
private readonly TaskDebouncer<Connections[]> _connectionsDebouncer = new TaskDebouncer<Connections[]>();
public ClassName()
{
_connectionsDebouncer.OnCompleted += OnConnectionUpdate;
}
public void OnConnectionUpdate(Connections[] connections, object sender)
{
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
foreach (var conn in connections)
RunOnMainThread(() => SearchResult.Add(new ConnectionUser(conn)));
}
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
_connectionsDebouncer.Add(RunInAsync(LoadData));
}
}
private async Task<Connection[]> LoadData()
{
return await connectionRepository
.GetConnections(currentPage, pageSize, searchPhrase)
.Where(conn => conn.Type != UserConnection.TypeEnum.Awaiting)
.ToArray();
}
It is not pretty clear what RunInAsync and RunOnMainThread methods are.
I guess, you don't actually need them.
I've created class for reusing thread. This class has one public method for queueing jobs.
public AwaitableJob<T> PrepareJob(Func<T> job)
{
lock (locker)
{
var aj = new AwaitableJob<T>(job);
taskQueue.Enqueue(aj);
System.Threading.Monitor.PulseAll(locker);
return aj;
}
}
AwaitableJob is custom class which should provide awaiter object.
public class AwaitableJob<T> : INotifyCompletion where T : class
{
public Func<T> Job { get; private set; }
public bool IsCompleted { get; private set; } = false;
private object result;
public AwaitableJob(Func<T> job)
{
this.Job = job;
}
public AwaitableJob<T> GetAwaiter()
{
return this;
}
public void Invoke()
{
result = Job.Invoke();
IsCompleted = true;
}
public object GetResult()
{
return result;
}
public void OnCompleted(Action continuation)
{
continuation.Invoke();
}
}
And I tried to use it this way
public async void Connect()
{
var atm = await Worker.PrepareJob(ConnectHelper) as PresentModel;
if (atm == null) return;
var vm = new SwitchingViewModel(atm);
vm.NavigateTo();
}
But instead of waiting it always continues in execution so atm variable is always null. When I added breakpoints into AwaitableJob it showed that GetResult is called before IsCompleted was setted to true. Anyone knows where could be problem? Thanks for your help.
The problem is here:
public void OnCompleted(Action continuation)
{
continuation.Invoke();
}
The point of OnCompleted is to register a callback for when it has completed, but you are invoking the continuation now. The only time you should do that is for the thread-race condition where somebody checks IsCompleted and gets false, but the status changes between that and registering the callback. Other than that, what you should be doing is storing the callback, and invoking it from the code that actually changes the status to completed (which looks like your Invoke method), again remembering to consider thread-safety around the switchover.
Frankly, if this sounds complex: use TaskCompletionSource<T>
I am experiencing some confusion with Tasks and the async/await key words. I understand that you should NOT mix async and blocking code. Or at least what my interpretation of mixing them is:
Don't make calls to blocking API's from non- async methods. So here's my issue.
I am trying to await a method, then update the UI accordingly. The issue is that the only way to await an async method() call is from within and async method().
Here's an example:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
await RunValidation(options, cancellationTokenSource.Token);
Completed = true;
}));
}
}
This code runs the method properly and awaits. The issue is when I return. For some reason when setting the Complete flag the buttons dependent on this flag are not toggled. If I comment the await code, then the buttons are toggled correctly. So assumed I was not returning on the UI thread, so I tried using this code instead:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
var context = TaskScheduler.FromCurrentSynchronizationContext();
await RunValidation(options, cancellationTokenSource.Token).ContinueWith(t => Completed = true, context);
//Completed = true;
}));
}
}
Here is the RunValidation() method:
private async Task RunValidation(Options options, CancellationToken token)
{
await _someService.SomAsyncMethod(options, token));
}
If you notice, the ExecuteCommand has an async key word before the (options) parameter that is passed to the command. If I remove the async key word then I have to modify the call to the RunValidation() method. I still need it to await, so this is what I did:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>((options) =>
{
Completed = false;
var context = TaskScheduler.FromCurrentSynchronizationContext();
var cancellationTokenSource = new CancellationTokenSource();
Task.Run(async () => await RunValidation(options, cancellationTokenSource.Token));
Completed = true;
}));
}
}
The problem with this code is that it doesn't await. So I am at a loss.
Can anyone shed some light on this for me please. I've spend 2 plus days on this and I am still here.
Thanks,
Tim
Here are the bindings to the Command Buttons.
private readonly Independent<bool> _completed = new Independent<bool>(true);
public bool Completed
{
get { return _completed; }
set { _completed.Value = value; }
}
private ICommand _doneCommand;
public ICommand DoneCommand
{
get
{
return _doneCommand ?? (_doneCommand = MakeCommand.When(() => Completed).Do(() =>
{
DoSomething();
}));
}
}
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ??
(_cancelCommand = MakeCommand.When(() => !Completed).Do(() => DoSomthingElse()));
}
}
I am using the MakeCommand objects from the UpdateControls library from Michael Perry. They contain dependancy tracking that raises the CanExecuteChange events when the Complete property is changed.
Your first code is correct. Most likely you have an incorrect implementation for your Completed property. Your view model object should implement INotifyPropertyChanged. The easiest way to do this right is use a base class that provides the functionality. ReactiveUI is the nuget package I always use. Usage is as simple as
public class MyObject : ReactiveObject {
private bool _Completed;
public bool Completed {
get => _Completed;
set => this.RaiseAndSetIfChanged(ref _Completed, value);
}
}
This will make sure that notifications are raised to the UI when the property is changed.
If you want it to be more magic you can use ReactiveUI.Fody and then your code will reduce to
public class MyObject : ReactiveObject {
[Reactive]
public bool Completed { get; set;}
}
So the issue was in fact the third party library. I was using it to provide dependency tracking for the 2 buttons. So when the complete flag changed it raised the CanExecuteChange events for both buttons without me having write code to do it. Unfortunately it stopped working after introducing the async/await calls. I replaced the 2 MakeCommands with RelayCommands and raised the events myself and everything worked.
So thanks to everyone for your responses.
Tim
I have a system which uses AOP with ContextBoundObject.
This is used to intercept a method call and perform certain operations before and after the function. It all works fine until I make the 'function to be intercepted' async.
I understand that the C# compiler rewrites async methods into a state machine, which returns control to the sink as soon as 'await' is reached
So it continues into the interception and executes the code which is meant to be executed only after the Method.
I can see there is an "AsyncProcessMessage" in IMessageSink, but I can't find a way to invoke it, and I am not sure if it will work in the async/await scenario.
Is there a way to make Async/Await work with the ContextBoundObject? Is using another Aspect Oriented Programming approach the only option here?
The code sample below has the method to be intercepted decorated with the 'Audit' attribute and placed in the AuditFacade which is a ContextBoundObject. The SyncProcessMessage method in the AuditSink has the logic to be executed before and after the method.
[AuditBoundary]
public class AuditFacade : ContextBoundObject
{
[Audit]
public ResponseObject DoSomthing()
{
//Do something
return new ResponseObject();
}
/// <summary>
/// Async Method to be intercepted
/// </summary>
/// <returns></returns>
[Audit]
public async Task<ResponseObject> DoSomthingAysnc()
{
//Do something Async
await Task.Delay(10000);
return new ResponseObject();
}
}
[AttributeUsage(AttributeTargets.Method)]
public class AuditAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
public class AuditBoundaryAttribute : ContextAttribute
{
public AuditBoundaryAttribute()
: base("AuditBoundary" + Guid.NewGuid().ToString())
{
}
public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
{
ctorMsg.ContextProperties.Add(new AuditProperty());
}
}
public class AuditProperty : IContextProperty, IContributeObjectSink
{
public string Name
{
get { return "AuditProperty"; }
}
public bool IsNewContextOK(Context newCtx)
{
var p = newCtx.GetProperty("AuditProperty") as AuditProperty;
if (p == null)
return false;
return true;
}
public void Freeze(Context newContext)
{
}
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new AuditSink(nextSink);
}
}
public class AuditSink : IMessageSink
{
private IMessageSink nextSink;
public AuditSink(IMessageSink nextSink)
{
this.nextSink = nextSink;
}
public IMessage SyncProcessMessage(IMessage msg)
{
var message = msg as IMethodCallMessage;
IMethodReturnMessage returnMessage = null;
ResponseObject response;
//Some Pre Processing happens here
var newMessage = new MethodCallMessageWrapper(message);
//Invoke the Method to be Audited
returnMessage = nextSink.SyncProcessMessage(newMessage) as IMethodReturnMessage;
response = returnMessage.ReturnValue as ResponseObject;
//Some Post Processing happens here with the "response"
return returnMessage;
}
public IMessageSink NextSink
{
get { return this.nextSink; }
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return nextSink.AsyncProcessMessage(msg, replySink);
}
}
I don't know anything about ContextBoundObject, but I think that AsyncProcessMessage() has nothing to do with async-await and that the following should work using the normal SyncProcessMessage():
Do your preprocessing step.
Invoke the async method.
Add your postprocessing step as a continuation to the returned Task, using ContinueWith() or await.
Return the continuation Task to the caller.
If you're okay with your postprocessing executing on the thread pool, then ContinueWith() is probably simpler. If you need the postprocessing to execute on the original context, use await.
The await version could look like this:
var responseTask = (Task<ResponseObject>)returnMessage.ReturnValue;
Func<Task<ResponseObject>> postProcessTaskFunc = async () =>
{
var response = await responseTask;
// Some Post Processing happens here with the "response"
return response;
}
return new ReturnMessage(postProcessTaskFunc(), …);
What'd be the most elegant way to call an async method from a getter or setter in C#?
Here's some pseudo-code to help explain myself.
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
There is no technical reason that async properties are not allowed in C#. It was a purposeful design decision, because "asynchronous properties" is an oxymoron.
Properties should return current values; they should not be kicking off background operations.
Usually, when someone wants an "asynchronous property", what they really want is one of these:
An asynchronous method that returns a value. In this case, change the property to an async method.
A value that can be used in data-binding but must be calculated/retrieved asynchronously. In this case, either use an async factory method for the containing object or use an async InitAsync() method. The data-bound value will be default(T) until the value is calculated/retrieved.
A value that is expensive to create, but should be cached for future use. In this case, use AsyncLazy from my blog or AsyncEx library. This will give you an awaitable property.
Update: I cover asynchronous properties in one of my recent "async OOP" blog posts.
You can't call it asynchronously, since there is no asynchronous property support, only async methods. As such, there are two options, both taking advantage of the fact that asynchronous methods in the CTP are really just a method that returns Task<T> or Task:
// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
get
{
// Just call the method
return MyAsyncMethod();
}
}
Or:
// Make the property blocking
public IEnumerable MyList
{
get
{
// Block via .Result
return MyAsyncMethod().Result;
}
}
I really needed the call to originate from the get method, due to my decoupled architecture. So I came up with the following implementation.
Usage: Title is in a ViewModel or an object you could statically declare as a page resource. Bind to it and the value will get populated without blocking the UI, when getTitle() returns.
string _Title;
public string Title
{
get
{
if (_Title == null)
{
Deployment.Current.Dispatcher.InvokeAsync(async () => { Title = await getTitle(); });
}
return _Title;
}
set
{
if (value != _Title)
{
_Title = value;
RaisePropertyChanged("Title");
}
}
}
You can use Task like this :
public int SelectedTab
{
get => selected_tab;
set
{
selected_tab = value;
new Task(async () =>
{
await newTab.ScaleTo(0.8);
}).Start();
}
}
I think that we can await for the value just returning first null and then get the real value, so in the case of Pure MVVM (PCL project for instance) I think the following is the most elegant solution:
private IEnumerable myList;
public IEnumerable MyList
{
get
{
if(myList == null)
InitializeMyList();
return myList;
}
set
{
myList = value;
NotifyPropertyChanged();
}
}
private async void InitializeMyList()
{
MyList = await AzureService.GetMyList();
}
I thought .GetAwaiter().GetResult() was exactly the solution to this problem, no?
eg:
string _Title;
public string Title
{
get
{
if (_Title == null)
{
_Title = getTitle().GetAwaiter().GetResult();
}
return _Title;
}
set
{
if (value != _Title)
{
_Title = value;
RaisePropertyChanged("Title");
}
}
}
Since your "async property" is in a viewmodel, you could use AsyncMVVM:
class MyViewModel : AsyncBindableBase
{
public string Title
{
get
{
return Property.Get(GetTitleAsync);
}
}
private async Task<string> GetTitleAsync()
{
//...
}
}
It will take care of the synchronization context and property change notification for you.
You can create an event and invoke an event when the property is changed.
Something like this:
private event EventHandler<string> AddressChanged;
public YourClassConstructor(){
AddressChanged += GoogleAddressesViewModel_AddressChanged;
}
private async void GoogleAddressesViewModel_AddressChanged(object sender, string e){
... make your async call
}
private string _addressToSearch;
public string AddressToSearch
{
get { return _addressToSearch; }
set
{
_addressToSearch = value;
AddressChanged.Invoke(this, AddressToSearch);
}
}
When I ran into this problem, trying to run an async method synchronicity from either a setter or a constructor got me into a deadlock on the UI thread, and using an event handler required too many changes in the general design.
The solution was, as often is, to just write explicitly what I wanted to happen implicitly, which was to have another thread handle the operation and to get the main thread to wait for it to finish:
string someValue=null;
var t = new Thread(() =>someValue = SomeAsyncMethod().Result);
t.Start();
t.Join();
You could argue that I abuse the framework, but it works.
Necromancing.
In .NET Core/NetStandard2, you can use Nito.AsyncEx.AsyncContext.Run instead of System.Windows.Threading.Dispatcher.InvokeAsync:
class AsyncPropertyTest
{
private static async System.Threading.Tasks.Task<int> GetInt(string text)
{
await System.Threading.Tasks.Task.Delay(2000);
System.Threading.Thread.Sleep(2000);
return int.Parse(text);
}
public static int MyProperty
{
get
{
int x = 0;
// https://stackoverflow.com/questions/6602244/how-to-call-an-async-method-from-a-getter-or-setter
// https://stackoverflow.com/questions/41748335/net-dispatcher-for-net-core
// https://github.com/StephenCleary/AsyncEx
Nito.AsyncEx.AsyncContext.Run(async delegate ()
{
x = await GetInt("123");
});
return x;
}
}
public static void Test()
{
System.Console.WriteLine(System.DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff"));
System.Console.WriteLine(MyProperty);
System.Console.WriteLine(System.DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff"));
}
}
If you simply chose System.Threading.Tasks.Task.Run or System.Threading.Tasks.Task<int>.Run, then it wouldn't work.
I think my example below may follow #Stephen-Cleary 's approach but I wanted to give a coded example. This is for use in a data binding context for example Xamarin.
The constructor of the class - or indeed the setter of another property on which it is dependent - may call an async void that will populate the property on completion of the task without the need for an await or block. When it finally gets a value it will update your UI via the NotifyPropertyChanged mechanism.
I'm not certain about any side effects of calling a aysnc void from a constructor. Perhaps a commenter will elaborate on error handling etc.
class MainPageViewModel : INotifyPropertyChanged
{
IEnumerable myList;
public event PropertyChangedEventHandler PropertyChanged;
public MainPageViewModel()
{
MyAsyncMethod()
}
public IEnumerable MyList
{
set
{
if (myList != value)
{
myList = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
}
}
}
get
{
return myList;
}
}
async void MyAsyncMethod()
{
MyList = await DoSomethingAsync();
}
}
I review all answer but all have a performance issue.
for example in :
string _Title;
public string Title
{
get
{
if (_Title == null)
{
Deployment.Current.Dispatcher.InvokeAsync(async () => { Title = await getTitle(); });
}
return _Title;
}
set
{
if (value != _Title)
{
_Title = value;
RaisePropertyChanged("Title");
}
}
}
Deployment.Current.Dispatcher.InvokeAsync(async () => { Title = await getTitle(); });
use dispatcher which is not a good answer.
but there is a simple solution, just do it:
string _Title;
public string Title
{
get
{
if (_Title == null)
{
Task.Run(()=>
{
_Title = getTitle();
RaisePropertyChanged("Title");
});
return;
}
return _Title;
}
set
{
if (value != _Title)
{
_Title = value;
RaisePropertyChanged("Title");
}
}
}
You can change the proerty to Task<IEnumerable>
and do something like:
get
{
Task<IEnumerable>.Run(async()=>{
return await getMyList();
});
}
and use it like
await MyList;