简体   繁体   中英

C# Downcasting with generics

In our project we are using .net WPF with MEF and prism. I have to do some task and as a part of it I have different classes of Model, all implementing IModel interface (let's call them Model1, Model2, Model3). For each model class I need to create a view model (ViewModel1, ViewModel2, ViewModel3). I would like to do it in some generic way so it will be extensible and if tomorrow someone will implement IModel in different dll that I doesn't know about, I still will be able to create a view model for it.

I though about something like this:

    [InheritedExport]
public interface IViewModelFactory<T>
{
    IViewModel Create(string id, IEnumerable<IModel> modelsList);
}

public class ViewModelFactory : IViewModelFactory<Model1>
{
    public IViewModel Create(string id, IEnumerable<IModel> modelsList)
    {
        return new ViewModel1(targetId, modelsList);;
    }
}

public class FactoryLocator
{
    public static IViewModelFactory<T> GetFactory<T>(T parameters)
    {
        return ServiceLocator.Current.GetInstance<IViewModelFactory<T>>();
    }
}

And then I will use it that way:

        public void CreateAndAddVM(IList<IModel> modelsList)
    {
        var factory = FactoryLocator.GetFactory(modelsList.FirstOrDefault());
        var vm = factory.Create(_id, modelsList);
        ViewModels.Add(vm);
    }

When I get to the FactoryLocator, the type is actually IModel interface and not a Model1/2/3.

Is there any way to make it work somehow? Or maybe another way to implement this requirement?

You should start with interface defined like this

public interface IViewModelFactory<T> where T : IModel 
{
    IViewModel Create(string id, IEnumerable<T> modelsList);
}

and it will actually force correct typing on future implementers.

As I mentioned in my comment. At this moment, anyone can create a view model with a model that may not be intended for it.

For example, if your ViewModel depends on Model1, there's nothing stopping me from providing a list of Model2 to the Create method in your factory.

Consider the following:

public abstract class ViewModelBase<TModel> where TModel : IModel

The example above will allow your ViewModel to specify what Model it represents, however this goes by the assumption that a ViewModel only has one Model, which may not always be the case.

In this scenario, your ViewModelBase class should have an ID, and a Model property for the TModel.

Ensure that IModel inherits from INotifyPropertyChanged , this will allow TModel to be an ObservableCollection .

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