简体   繁体   中英

Generics and Extension Methods - how does IntelliSense infer types?

I've got a question about how the compiler infers types when using Generics and Extension Methods. The easiest way to ask my question is to show some code first...

I've got a ViewModelBase class that looks a bit like this (with everything that is not relevant taken out). Basically my NavigationService class calls the NavigatingTo method each time the application goes to the View that it is linked up with.

Inheriters of this class can call the Return delegate to pass data back to the caller.

public abstract class ViewModelBase<TContext, TReturn>
{
    public Action<TReturn> Return { get; set; }

    public abstract void NavigatingTo(TContext context);
}

Then I have a test ViewModel that inherits ViewModelBase:

public class TestViewModel : ViewModelBase<int, bool>
{
    public override void NavigatingTo(int context)
    {
        // do stuff here
    }
}

Next I have a generic NavigationCommand class that accepts a ViewModelBase like this:

public class NavigationCommand<TViewModel>
{
    public TViewModel ViewModel { get; set; }

    public NavigationCommand(TViewModel viewModel)
    {
        this.ViewModel = viewModel;
    }
}

And lastly I have an Extension Method for the NavigationCommand class to add a Navigate method to it. My aim here is that by stating that my Navigate method requires a ViewModelBase with TContext and TReturn, the compiler should be able to infer what types are actually to be used:

public static class NavigationExtensions
{
    // I actually pass in a INavigationService here too, but I have left that out to
    // make it easier to read...

    public static void Navigate<TViewModel, TContext, TReturn>(
        this NavigationCommand2<TViewModel> viewModel, 
        TContext context, 
        Action<TReturn> returnAction) 
        where TViewModel : ViewModelBase<TContext, TReturn>
    {
        // actual implementation omitted
    }
}

Ok, so now in my ApplicationController class I do the following:

var vm = new TestViewModel();
var cmd = new NavigationCommand2<TestViewModel>(vm);

int clientID = 1;

Action<bool> returnAction = success =>
{
    Console.WriteLine(success.ToString());
};

cmd.Navigate(clientID, returnAction);

It works, and if you try to pass in an incorrect type you get a compiler error when you build. However Intellisense does not prompt you for the correct types.

So my question: Is there any way to rewrite the extension method, or my NavigationCommand class or ViewModel, etc so that Intellisense actually prompts me to use the correct type?

Currently all Intellisense gives me is this:

(extension void) NavigateCommand<TestViewModel>.Navigate(TContext context, Action<TReturn> returnAction)

When what I want is this:

(extension void) NavigateCommand<TestViewModel>.Navigate(int context, Action<bool> returnAction)

One of the ways to solve this issue is to propagate TContext and TReturn type parameters to the NavigationCommand, so its declaration will look like:

public class NavigationCommand<TViewModel, TContext, TReturn> where TViewModel:ViewModelBase<TContext, TReturn>

But it makes the command initialization more verbose (because the TestViewModel type in fact already includes the information about TContext and TReturn actual types):

var cmd = new NavigationCommand<TestViewModel, int, bool>(vm);

Actually, the implementation you have posted is already type-safe and will not allow you to pass the argument of incorrect type (let's say string instead of int ). The only issue is Intellisense that is not able to infer the type correctly for some reason.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM