que - propiedades c#
¿Cómo llamar a un método asíncrono desde un getter o un setter? (8)
¿Cuál sería la forma más elegante de llamar a un método asíncrono desde un getter o setter en C #?
Aquí hay un pseudo código para ayudarme a explicarme.
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
Como su "propiedad asíncrona" está en un modelo de vista, puede usar AsyncMVVM :
class MyViewModel : AsyncBindableBase
{
public string Title
{
get
{
return Property.Get(GetTitleAsync);
}
}
private async Task<string> GetTitleAsync()
{
//...
}
}
Cuidará el contexto de sincronización y la notificación de cambio de propiedad.
Creo que podemos esperar el valor simplemente volviendo primero nulo y luego obtener el valor real, por lo que en el caso de Pure MVVM (proyecto PCL por ejemplo) creo que la siguiente es la solución más elegante:
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();
}
No hay ninguna razón técnica para que las propiedades de async
no estén permitidas en C #. Fue una decisión de diseño decidida, porque "propiedades asíncronas" es un oxímoron.
Las propiedades deben devolver los valores actuales; no deberían estar iniciando operaciones de fondo.
Generalmente, cuando alguien quiere una "propiedad asincrónica", lo que realmente quiere es uno de estos:
- Un método asíncrono que devuelve un valor. En este caso, cambie la propiedad a un método
async
. - Un valor que puede usarse en el enlace de datos, pero debe calcularse / recuperarse de forma asincrónica. En este caso, utilice un método de fábrica
async
para el objeto que lo contiene o utilice unasync InitAsync()
. El valor vinculado a los datos serádefault(T)
hasta que se calcule / recupere el valor. - Un valor que es caro de crear, pero debe almacenarse en caché para uso futuro. En este caso, use
AsyncLazy
de mi blog o biblioteca AsyncEx . Esto te dará una propiedad que teawait
.
Actualización: cubro las propiedades asíncronas en una de mis publicaciones recientes de blog "async OOP".
No puede llamarlo de forma asincrónica, ya que no hay soporte de propiedad asíncrono, solo métodos asíncronos. Como tal, hay dos opciones, ambas aprovechando el hecho de que los métodos asíncronos en el CTP son solo un método que devuelve Task<T>
o Task
:
// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
get
{
// Just call the method
return MyAsyncMethod();
}
}
O:
// Make the property blocking
public IEnumerable MyList
{
get
{
// Block via .Result
return MyAsyncMethod().Result;
}
}
Pensé que .GetAwaiter (). GetResult () era exactamente la solución a este problema, ¿no? p.ej:
string _Title;
public string Title
{
get
{
if (_Title == null)
{
_Title = getTitle().GetAwaiter().GetResult();
}
return _Title;
}
set
{
if (value != _Title)
{
_Title = value;
RaisePropertyChanged("Title");
}
}
}
Puede cambiar la libertad a Task<IEnumerable>
y haz algo como:
get
{
Task<IEnumerable>.Run(async()=>{
return await getMyList();
});
}
y úsalo como si esperaras MyList;
Realmente necesitaba la llamada para originar desde el método get, debido a mi arquitectura desacoplada. Así que se me ocurrió la siguiente implementación.
Uso: el título está en un modelo de vista o un objeto que puede declarar estáticamente como un recurso de página. Vincúlate y el valor se completará sin bloquear la interfaz de usuario, cuando getTitle () regrese.
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");
}
}
}
Una cosa a tener en cuenta: en el caso en que se devuelva un resultado de IEnumerable, el retorno de rendimiento tiene un mejor rendimiento y no requiere la conversión a IEnumerable.
public IEnumerable MyList
{
get
{
yield return MyAsyncMethod();
}
}