我有这样的东西:
interface IProduct { }
class ProductA : IProduct { }
class ProductB : IProduct { }
....
interface IViewModel { }
class ProductAViewModel : IViewModel { }
class ProductBViewModel : IViewModel { }
现在,我希望能够创建ViewModel的实例,而无需了解产品的真实类型:
IProduct prod = new ProductA();
IViewModel vm = someFactoryOrBuilderObject.CreateViewModel(prod);
// real type of vm depends on the product's type
// if prod is ProductA, vm must be ProductAViewModel
...
DisplayViewModel(VM); // working with VM as IViewModel (regardless of it's real type)
目标:
ViewModel的类型必须在运行时解析。
向我的应用添加新产品和Viewmodel不需要更改现有代码。
哪种是实现此功能的最佳方法? 有一些显而易见的方法,但并不令人满意:
答案 0 :(得分:1)
警告:这可能不是遵循的良好设计模式。
在实践中,依赖关系链应为View
=> ViewModel
=> Model
(=>
表示“依赖”)。 Model
对ViewModel
一无所知(即通过属性,在下面的代码中)。
下面的答案仅是您在问题中所要求的内容。
我认为不使用反射就没有真正的“干净”方法。但是,我们可以使用反射使其余代码干净(其价格是使用反射:))。
我现在提出使用属性的解决方案。
请记住:仅当您为每个ViewModel
类都具有一个Product
对象作为参数的公共构造函数时,此方法才有效!否则,Varun的解决方案是更好(在这种情况下,您仍然需要一个映射功能)。
using System;
using System.Diagnostics;
using System.Linq;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
sealed class UseViewModelAttribute : Attribute
{
public Type ViewModelType { get; }
public UseViewModelAttribute(Type viewModelType)
{
Debug.Assert(typeof(IViewModel).IsAssignableFrom(viewModelType));
ViewModelType = viewModelType;
}
}
class ViewModelFactory
{
private readonly static Type[] ctorParams = new Type[] { typeof(IProduct) };
public IViewModel CreateViewModel(IProduct product)
{
var vmType = (product.GetType().GetCustomAttributes(false).FirstOrDefault(attr
=> attr is UseViewModelAttribute) as UseViewModelAttribute)?.ViewModelType;
Debug.Assert(!(vmType is null));
return Activator.CreateInstance(vmType, product) as IViewModel;
}
}
要使用此功能,请在您的UseViewModel(typeof(ViewModelForThisProductType))
类之前添加Product
。喜欢:
[UseViewModel(typeof(ProductAViewModel))]
class ProductA : IProduct { }
[UseViewModel(typeof(ProductBViewModel))]
class ProductB : IProduct { }
您的ViewModel
类应类似于:
class ProductAViewModel : IViewModel {
//You can use both IProduct or the derived type (i.e ProductA) as the constructor param
public ProductAViewModel(ProductA a)
{
}
}
class ProductBViewModel : IViewModel {
public ProductBViewModel(IProduct b)
{
}
}
答案 1 :(得分:0)
如果您只想在运行时执行此操作,那么我看到的唯一选择是按名称约定进行映射。在CreateViewModel内,您将搜索如下内容:
IViewModel CreateViewModel(IProduct product)
{
var vmType = Assembly.getTypes().SingleOrDefault(x => x.Name == $"{product.GetType().Name}ViewModel");
// Create instance of vmType and perform mapping of properties between product and vm using reflection based on name convention. Automapper can do this for you as well.
}