15

Possible Duplicate:
How to call an async method from a getter or setter?

I'm trying to implement a property that'll use the Sqlite await inside it:

public int ID {
        get
        {
            if (_id != null)
            {
                return _id;
            }
            if (string.IsNullOrEmpty(ImageName))
            {
                return -1;
            }
            var query = CurrentConnection.Table<Image>().Where(i => i.ImageName == ImageName);
            var result = await query.ToListAsync();
            ...other code

However, since a property is not set to await(tried that, doesn't work), I can't use await inside a property.

Any way to get around this besides using a method instead of a property?

Community
  • 1
  • 1
jbkkd
  • 1,510
  • 5
  • 18
  • 37

4 Answers4

12

No - there's no such thing as an async property. Even if you could declare an async property, it would have to be declared as having a return value of Task<int> rather than int... otherwise what would you want to return when you hit an await expression?

I would strongly suggest a GetIdAsync method here, assuming you definitely want async behaviour.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    Also note that if it's a property of type `Task` it would make setters *really* weird. – Servy Dec 05 '12 at 15:49
  • 1
    Since async/await can't be used in conjunction with yield return blocks, a sensible compromise would have been manually implementing IEnumerator>, where the Current property is async. This rule that async can't be used on properties is a stylistic constraint that precludes this useful pattern. The only solution now is to expose *internal* iteration details to clients so they can perform the iteration manually. Horrible. – naasking Sep 26 '15 at 14:06
11

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.
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
4

You should not use a property Getter for anything besides just returning a variable value anyway.

Calling code often makes performance-wise assumptions of what to expect when calling your classes.

Rule of thumb:

  • Properties imply a very fast return of a value
  • Methods imply more or less lengthy operations

Therefore it is strongly recommended not to put any lengthy logic into a getter. Use methods instead.

Microsofts's Rico Mariany gives a very detailed article about it here: Performance Guidelines for Properties

Jens H
  • 4,590
  • 2
  • 25
  • 35
  • Service fabric has async dictionaries to get state objects -- it's very fast, and they do it all over the damn place in code samples. -- It seems like 90% of the time it's a very very cheap operation. -- But it's so much more code to write than just grabbing a property. -- There needs to be a solution to that. :( – BrainSlugs83 May 09 '17 at 23:37
  • @BrainSlugs83: Do I understand correctly that you are downvoting me because you do not like Microsoft's implementation of reliable collection...? – Jens H May 10 '17 at 08:36
0

If you use await, you are turning the rest of the method into a callback, which means you cannot return an int. You would need to return an Task<int>. Currently there is also no support for using await in a property, you'd need to convert the property into a "get" method returning the Task<int>.

If you want to return the int directly, instead of doing await awaitable, use awaitable.Result. This will cause the asyncronous action to resolve syncronously, allowing you to return the concrete result directly.

SecurityMatt
  • 6,593
  • 1
  • 22
  • 28
  • 3
    However performing a database query inside of a property getter is generally considered a poor idea. Property getters should be fast and simple. – Servy Dec 05 '12 at 15:50
  • It depends on what the property's main purpose is. If the object is a DbLoggedInUser and the property is FirstName, then it would seem reasonable for the property to perform a database query in the background. However you are right that in general properties should be simple and not be able to fail. – SecurityMatt Dec 05 '12 at 21:48
  • It would never seem reasonable to do a database query in the background of a property's getter. -- That's just wrong. -- That *needs* to be a method call. – BrainSlugs83 May 09 '17 at 23:39