342

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
    }
}
poke
  • 369,085
  • 72
  • 557
  • 602
Doguhan Uluca
  • 6,933
  • 4
  • 38
  • 51
  • 6
    My question would be why. A property is supposed to mimic something like a field in that it typically should perform little (or at least very quick) work. If you have a long-running property its much better to write it as a method so the caller knows it's a more complex body of work. – James Michael Hare Jul 06 '11 at 20:17
  • @James: That's exactly right - and I suspect that's why this was explicitly not supported in the CTP. That being said, you can always make the property of type `Task`, which will return immediately, have normal property semantics, and still allow things to be treated asynchronously as needed. – Reed Copsey Jul 06 '11 at 20:20
  • 24
    @James My need arises from using Mvvm and Silverlight. I want to be able to bind to a property, where the loading of the data is done lazily. The ComboBox extension class I'm using requires the binding to happen at the InitializeComponent() stage, however the actual data load happens much later. In trying to accomplish with as little code as possible, getter and async feels like the perfect combination. – Doguhan Uluca Jul 07 '11 at 14:33
  • related: http://stackoverflow.com/a/33942013/11635 – Ruben Bartelink Nov 26 '15 at 20:32
  • James and Reed, Your seem to be forgetting that there are edge cases always. In the case of WCF, I wanted to verify that the data being placed on a property is correct and it had to be verified using encryption/decryption. The functions I use for decryption happen to employ an async function from a third party vendor. (NOT MUCH I CAN DO TO HERE). – RashadRivera Aug 17 '17 at 00:17
  • @RashadRivera it's been a while since I've done WCF or Silverlight, but don't they support binding to async properties that return `Task`? – phoog Mar 15 '19 at 15:29
  • Heres a simple example why someone would be calling an async method in a property. In Xamarin Essentials, to return a value from SecureStorage, you need to use GetAsync. This value could then be bound via a view model to XAML. Hence the view model property needs to make that call to secure storage. – James Westgate Oct 28 '19 at 11:26
  • 1
    I know your example returns an Enumerable, but if your async method only returns a Task you could change it to return void and call it from setter. – Michael G Nov 04 '20 at 17:22

14 Answers14

320

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:

  1. An asynchronous method that returns a value. In this case, change the property to an async method.
  2. 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.
  3. 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.

Dale M
  • 2,453
  • 1
  • 13
  • 21
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 5
    In point 2. imho you do not take into account the regular scenario where setting the property should *again* initialize the underlying data (not only in the constructor). Is there any other way besides using *Nito AsyncEx* or using `Dispatcher.CurrentDispatcher.Invoke(new Action(..)`? – Gerard Nov 19 '13 at 11:14
  • @Gerard: I don't see why point (2) wouldn't work in that case. Just implement `INotifyPropertyChanged`, and then decide whether you want the old value returned or `default(T)` while the asynchronous update is in flight. – Stephen Cleary Nov 19 '13 at 11:57
  • 1
    @Stephan: ok, but when I call the async method in the setter I get the CS4014 "not awaited" warning (or is that only in Framework 4.0?). Do you advice to suppress that warning in such a case? – Gerard Nov 19 '13 at 12:11
  • @Gerard: My first recommendation would be to use the [`NotifyTaskCompletion` from my AsyncEx project](https://nitoasyncex.codeplex.com/wikipage?title=NotifyTaskCompletion&referringTitle=Documentation). Or you can build your own; it's not that hard. – Stephen Cleary Nov 19 '13 at 12:18
  • 2
    @Stephan: ok will try that. Maybe a nice article about this asynchronous databinding-viewmodel-scenario is in place. E.g. binding to `{Binding PropName.Result}` is not trivial for me to find out. – Gerard Nov 19 '13 at 13:47
  • @Gerard: Yes, I'm planning on writing some `async` articles specifically with MVVM in mind. Thanks for the prodding. :) – Stephen Cleary Nov 19 '13 at 13:48
  • @Stephan: it works, I was surprised that `propertyChanged(..."Result")` was ok for a databinding against `PropName.Result`., I expected that `propertyChanged(..."PropName.Result")` was needed. – Gerard Nov 19 '13 at 14:43
  • @Stephan: I still have an issue where `PropertyChanged` is sometimes null and it only works when I manually add `propertyChanged(..."PropName")` without the `.Result`: can I create a new question about it? – Gerard Nov 19 '13 at 16:09
  • @Gerard: Your VM should not be raising `PropertyChanged` for `Result` - only `PropName`. Please do create a new question. – Stephen Cleary Nov 19 '13 at 16:09
  • @Stephan: posted a question: the async model works very nicely, but I am really curious to understand :) – Gerard Nov 19 '13 at 16:34
  • Is calling an asynchronous method in the set method of a property also an oxymoron? – Hong Feb 24 '15 at 19:43
  • @Hong: I would say that should be a method. If a property setter does something complex enough to be an asynchronous operation, then that property should probably be a method. – Stephen Cleary Feb 24 '15 at 19:55
  • There is a builtin `AsyncLazy` now. It is in Assembly `Microsoft.VisualStudio.Threading` look [here](https://msdn.microsoft.com/en-us/library/dn268899.aspx) – Legends Aug 29 '16 at 07:48
  • I just came across this while looking for a solution to my issue. I have a property that returns a constructed string based on other member values in the class. In this case I have an IQueryable property member with a result in that may have not yet been evaluated. The result string should have the count of this IQueryable inside of it but I cannot do this as part of a property using the CountAsync function. This doesn't seem like a bad use of a property to me. – Stephen Foster Nov 07 '16 at 08:13
  • @StephenFoster: Sure, a property can be used to synchronously build a *query* that is (asynchronously) executed later. The property itself is still synchronous. – Stephen Cleary Nov 07 '16 at 11:27
  • @StephenCleary I have a wpf application where ActualPageNumber property is used with databinding. So when I change the property, the setter should access the db which can be slow. That is why I wanted an async setter, to keep the gui responsive. – Istvan Heckl Oct 02 '20 at 13:53
  • @IstvanHeckl: `DataGrid` (and `DataTable`) do not work very nicely with `async`. I recommend posting your own separate question. – Stephen Cleary Oct 04 '20 at 02:03
  • @StephenCleary Dear Stephen, As suggested I create a separate question here: https://stackoverflow.com/q/64203162/5852947 – Istvan Heckl Oct 05 '20 at 06:04
  • Maybe it used to be correct to say properties return immediate values and don't kick off background processes. But for whatever reason, C# properties consist of setter and getter functions. For right or wrong, they can kick of tasks. And it would be helpful if they could be async. – Jonathan Wood Dec 10 '21 at 19:29
  • To 2) How would you call InitAsync from within a synchronous method? – Jens Mander Dec 30 '22 at 15:53
  • @JensMander: (2) is dealing with data binding; I recommend you read [my article on the subject](https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/march/async-programming-patterns-for-asynchronous-mvvm-applications-data-binding). – Stephen Cleary Dec 30 '22 at 19:28
  • the problem is that in several places in .NET, you are required to make something into a property. so converting something to an asynchronous method is not a solution – symbiont Feb 21 '23 at 20:42
  • @symbiont: If you have a specific scenario, please post your own question. This answer provides general advice. – Stephen Cleary Feb 21 '23 at 22:25
129

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;
    }
}
poke
  • 369,085
  • 72
  • 557
  • 602
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 3
    Thank you for your response. Option A: Returning a Task doesn't really workout for binding purpose. Option B: .Result, as you mention, blocks the UI thread (in Silverlight), therefore requiring the operation to execute on a background thread. I'll see if I can come up with a workable solution with this idea. – Doguhan Uluca Jul 07 '11 at 14:49
  • 4
    @duluca: You could also try having a method that's like `private async void SetupList() { MyList = await MyAsyncMethod(); }` This would cause MyList to be set (and then automatically bind, if it implements INPC) as soon as the async operation completes... – Reed Copsey Jul 07 '11 at 15:52
  • The property needs to be in an object that I'd declared as a page resource, so I really needed this call originate from the getter. Please see my answer for the solution I came up with. – Doguhan Uluca Jul 07 '11 at 16:17
  • 2
    @duluca: That was, effectively, what I was suggesting you do... Realize, though, this if you access Title multiple times quickly, your current solution will lead to multiple calls to `getTitle()` simulatenously... – Reed Copsey Jul 07 '11 at 17:54
  • Very good point. Although not a problem for my specific case, a boolean check for isLoading would fix the issue. – Doguhan Uluca Jul 07 '11 at 18:47
  • The question is asking for ".Result" method. +1 For explaining that it is blocking. – Jeson Martajaya Jun 04 '15 at 17:58
  • Will there ever be support implemented for `async get` and `async set`? – Will Jul 20 '15 at 22:17
  • 1
    @Will No plans that I've seen – Reed Copsey Jul 20 '15 at 23:36
  • Why `.Result`, rather use `await GetLanguagesAsync()`? – Mafii Mar 23 '16 at 08:21
  • 1
    @mafii there's no support for async in properties – Reed Copsey Mar 23 '16 at 15:41
  • @ReedCopsey -- The blocking property could deadlock in some case (e.g. UI) correct? – rory.ap Mar 27 '19 at 16:45
63

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");
        }
    }
}
Doguhan Uluca
  • 6,933
  • 4
  • 38
  • 51
  • 9
    **updfrom 18/07/2012** in Win8 RP we should change Dispatcher call to: *Window.Current.CoreWindow.Dispatcher.RunAsync( CoreDispatcherPriority.Normal, async () => { Title= await GetTytleAsync(url);});* – Anton Sizikov Jul 18 '12 at 11:23
  • Shouldn't the lambda method be `() => { _Title = await getTitle(); }` ? – Christopher Stevenson May 14 '13 at 21:33
  • 7
    @ChristopherStevenson, I thought so too, but I don't believe that is the case. Because the getter is being executed as a fire and forget, without calling the setter upon completion the binding will not be updated when the getter has finished excuting. – Iain May 20 '13 at 05:41
  • 1
    This answer contains bugs, getter of "Title" property creates a race condition. InvokeAsync method of Dispatcher is a async method, it returns before is completes. – Medeni Baykal Aug 19 '15 at 12:06
  • Did you try @updfrom's recommendation? Since the original answer, the .NET framework and Microsoft's various front-end technologies have evolved quite a bit, so I wouldn't be surprised if there's now a race condition where one didn't exist before. – Doguhan Uluca Aug 19 '15 at 16:18
  • 3
    No, it has and had a race condition, but user won't see it because of 'RaisePropertyChanged("Title")'. It returns before it completes. But, after completion you're setting the property. That fires PropertyChanged event. Binder gets value of property again. – Medeni Baykal Sep 14 '15 at 21:51
  • 1
    Basically, the first getter will return a null value, then it will get updated. Note that if we want getTitle to be called each time, there could be a bad loop. – tofutim Feb 17 '17 at 22:16
  • 4
    You should also be aware that any exceptions in that async call will be completely swallowed. They don't even reach your unhandled exception handler on the application if you have one. – Philter Apr 06 '18 at 15:45
16

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();
            }
        }
MohsenB
  • 1,669
  • 18
  • 29
10

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();
}
Juan Pablo Garcia Coello
  • 3,192
  • 1
  • 23
  • 33
  • Thanks, I think is the simplest and it just works, it's like a magic trick :). – Juan Pablo Garcia Coello Jul 24 '15 at 06:42
  • 3
    Doesn't this generate compiler warnings `CS4014: Async method invocation without an await expression` – Nick Sep 04 '15 at 14:11
  • 8
    Be *very* skeptical of following this advice. Watch this video then make your own mind up: https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only. – Contango Nov 05 '15 at 10:49
  • Apparently this only works for initialization? Once the list has been calculated the first time it will not be null anymore. And if you remove the condition in the getter you will end up in an infinite loop since the getter calls the setter and NotifyPropertyChanged will call the getter again. – Cocomico Mar 07 '16 at 16:53
  • After the first time you have to call MyList = await AzureService.GetMyList(); or whatever in the code and the UI will get the update. The infinite loops only happens if you misspell the name of the field lowercase with the name of the property Uppercase (I guess) – Juan Pablo Garcia Coello Mar 07 '16 at 18:08
  • 2
    You SHOULD avoid using "async void" methods! – SuperJMN Jan 26 '17 at 12:06
  • 2
    every shout should come with a wise reply, would you @SuperJMN explain us why? – Juan Pablo Garcia Coello Jan 26 '17 at 16:54
  • @JuanPabloGarciaCoello Please, check: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx. Tip: you cannot handle exceptions ;) – SuperJMN Jan 26 '17 at 20:43
  • @SuperJMN is from 2013, is still valid? is there a newer documentation? – Juan Pablo Garcia Coello Jan 29 '17 at 08:24
  • Of course it is. More info about the reasons: https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only – SuperJMN Jan 29 '17 at 12:08
  • @Nick `async void` is not awaitable – HappyNomad Sep 21 '17 at 12:43
  • 1
    @Contango Good video. He says, "Use `async void` only for top-level handlers and their like". I think this may qualify as "and their like". – HappyNomad Sep 21 '17 at 13:39
  • for Pcl you can just create a async command and call execute on it. this isnt an optimal implementation – Emil Mar 29 '18 at 09:22
  • if you call the getter multiple times, before the `InitializeMyList` finishes, then the list will be initialized multiple times. There should be a flag indicating if the list is being initialized. – PLopes Nov 08 '22 at 11:46
5

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");
        }
    }
}
bc3tech
  • 1,228
  • 14
  • 27
  • 12
    That's the same as just blocking with `.Result` -- it isn't async and it can result in deadlocks. – McGuireV10 Dec 09 '17 at 18:01
  • you need add IsAsync=True – Alexsandr Ter Mar 13 '19 at 20:56
  • 1
    I appreciate the feedback on my answer; I'd really love for somebody to provide an example where this deadlocks so I can see it in action – bc3tech Mar 30 '20 at 16:23
  • 1
    This sounded right for my situation, but when I added GetAwaiter().GetResult() the whole app locked up never to be used again... (instant 100% deadlock) I can't fully explain that, but if I can think of a small sample case I'll do it – PandaWood Oct 18 '22 at 04:10
3

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

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.

Dmitry Shechtman
  • 6,548
  • 5
  • 26
  • 25
  • Being a property, it has to be. – Dmitry Shechtman Aug 07 '15 at 08:33
  • Sorry, but maybe I missed the point of this code then. Can you elaborate please? – Patrick Hofman Aug 07 '15 at 08:33
  • Properties are blocking by definition. GetTitleAsync() serves as an "async getter" sans the syntactic sugar. – Dmitry Shechtman Aug 07 '15 at 08:38
  • 1
    @DmitryShechtman: No, it does not have to be blocking. This is exactly what change notifications and state machines are for. And they are not blocking by definition. They are synchronous by definition. This is not the same as blocking. "Blocking" means they may do heavy work and may consume considerable time to execute. This in turn is exactly what properties SHOULD NOT DO. – quetzalcoatl Jul 07 '18 at 17:12
1

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.

Yuval Perelman
  • 4,499
  • 1
  • 22
  • 32
0

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.

Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • 5
    What is `Nito.AsyncEx.AsyncContext.Run`? Where can I find it? Is it an external dependency? These are important details that you should edit into this answer to make it better. – Ian Kemp Oct 14 '20 at 10:19
  • FYI: Nito.AsyncEx is Stephen Cleary's library which he linked to earlier in a reply (or a comment) on this thread. Here's the current github link. https://github.com/StephenCleary/AsyncEx – Joe Dec 24 '20 at 18:26
-1

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


}
Craig.C
  • 561
  • 4
  • 17
  • 1
    Calling async void is a terrible idea as it doesn't block and any exceptions will go into a black hole – rollsch Jun 27 '21 at 10:22
  • @rolls Good reasons to avoid. Still just looking I can't see an approved or upvoted coded example. I for one would happy have a good answer for ref and would upvote. – Craig.C Jul 20 '21 at 14:22
-1

In .NET Core you can use it just like this:

InvokeAsync(async () => { await MyAsyncMethod(); });

So the complete getter/setter should like that:

private bool _myBoolProperty;
public bool MyBoolProperty
{
    get { return _myBoolProperty; }
    set
    {
        _myBoolProperty = value;
        InvokeAsync(async () => { await MyAsyncMethod(); });
    }
}
CoolWolf
  • 19
  • 5
-2

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

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;