Rikube
Rikube

Reputation: 81

Cannot convert anonymous method block to delegate because some of the return types in the block are not implicitly convertible

I want to refactor some functions that create asynchronously ViewModels. They were looking as follow :

public async Task NavigateToCreateFormVM()
{
    try
    {
        IsLoading = true;
        CurrentVM = await Task.Run(() => new CreateFormVM());
        <...>
        IsLoading = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "App", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

So in order to refactor all the logic, I tried writing a delegate to keep the code easy t maintain.

public async Task NavigationDelegate(Func<Task<BaseVM>> delegate)
{
    try
    {
        IsLoading = true;
        CurrentVM = await delegate();
        <...>
        IsLoading = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "App", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

and for example change my functions to :

public async Task NavigateToCreateFormVM()
{
    await NavigationDelegate(() => Task.Run(() => new CreateFormVM()));
}

public async Task NavigateToConsultFormVM()
{
    await NavigationDelegate(() => ConsultFormVM.CreateAsync()); // type of ConsultFormVM
}

But I get two error messages indicating that they can't convert CreateFormVM/ConsultFormVM to BaseVM (they both inerith from BaseVM). If i change NavigationDelegate(Func<Task<BaseVM>> delegate) to NavigationDelegate(Func<Task<CreateFormVM>> delegate) it works with no problems.

The errors messages are CS0029 and CS1662.

Cannot implicitly convert type 'System.Threading.Tasks.Task<Project.CreateFormVM>' to 'System.Threading.Tasks.Task<Project.BaseVM>' Cannot convert anonymous method block to delegate type because some of the return types in the block are not implicitly convertible to the delegate return type

I don't know if I'm missing something or if I did something wrong (or if it shouldn't be done this way at all ?) so I'd be grateful if soemone knew how to resolve my problem.

Upvotes: 0

Views: 315

Answers (2)

Johnathan Barclay
Johnathan Barclay

Reputation: 20373

As @Eldar points out, TResult is invariant in Task<TResult>, as with all generic classes in .Net.

This means that you can't pass a function that returns Task<CreateFormVM> in place of a Func<Task<BaseVM>>.

However, there is a way to define NavigationDelegate in a way that will achieve your desired behaviour, using generic type constraints:

public async Task NavigationDelegate<TBaseVM>(Func<Task<TBaseVM>> delegate)
where TBaseVM : BaseVM
{
    try
    {
        IsLoading = true;
        CurrentVM = await delegate();
        <...>
        IsLoading = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show(
            ex.Message,
            "App",
            MessageBoxButton.OK,
            MessageBoxImage.Error);
    }
}

Now you can pass any function that returns a Task<TResult>, who's TResult derives from TBaseVM.

Including this:

public async Task NavigateToCreateFormVM()
{
    await NavigationDelegate(() => Task.Run(() => new CreateFormVM()));
}

The constraint ensures that result of awaiting the delegate will be assignable to BaseVM, meaning this is safe:

CurrentVM = await delegate();

Upvotes: 2

Eldar
Eldar

Reputation: 10790

Func<T>'s type argument is not contravariant and Task<T>'s type argument is not covariant. So you can not do these :

    public class Base
    {
    }

    public class Derived : Base
    {
    }

    public static void Main()
    {
        Func<Task<Base>> d = GetBase;
        Func<Task<Derived>> e = d; // Will fail
        Task<Base> dd = GetDerived(); // Will fail
    }

    public static Task<Base> GetBase()
    {
        return Task.FromResult(new Base());
    }

    public static Task<Derived> GetDerived()
    {
        return Task.FromResult(new Derived());
    }

Fiddle

What is covariance and contravariance

What you can do is to change ConsultFormVM.CreateAsync() methods return type to Task<BaseVM>.

Upvotes: 1

Related Questions