2009-06-22 31 views
2

我有一个WPF应用程序,其中PageItems是模型对象。如何模拟多重继承并使用反射来优化此代码?

我的主ViewModel的ObservableCollection为PageItemViewModels,每个人都从其匹配的PageItem模型对象构建自己。

每个PageItemViewModel从抽象类BaseViewModel继承,以获得INotifyPropertyChanged的功能。

每个PageItemViewModel也实现了IPageItemViewModel,以确保它具有所需的性能。

我最终将有大约50页,所以我想消除任何不必要的代码

  • 解决(见下文):有没有办法,我可以得到PageItemViewModel类继承IDCODE和标题所以我不必在每个班级实施它们?我不能将它们放在BaseViewModel中,因为其他ViewModel继承了它,它们不需要这些属性,我不能将它们放在IPageItemViewModel中,因为它只是一个接口。我明白我需要多重继承这其中C#不支持
  • 解决(见下文):有没有方法可以让我摆脱开关语句,例如莫名其妙地用反射代替?

下面是一个独立的控制台应用程序这表明我的代码在我WPF应用:

using System.Collections.Generic; 

namespace TestInstantiate838 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      List<PageItem> pageItems = PageItems.GetAll(); 
      List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>(); 

      foreach (PageItem pageItem in pageItems) 
      { 
       switch (pageItem.IdCode) 
       { 
        case "manageCustomers": 
         pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem)); 
         break; 
        case "manageEmployees": 
         pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem)); 
         break; 
        default: 
         break; 
       } 
      } 
     } 
    } 

    public class PageItemManageCustomersViewModel : ViewModelBase, IPageItemViewModel 
    { 
     public string IdCode { get; set; } 
     public string Title { get; set; } 

     public PageItemManageCustomersViewModel(PageItem pageItem) 
     { 

     } 
    } 

    public class PageItemManageEmployeesViewModel : ViewModelBase, IPageItemViewModel 
    { 
     public string IdCode { get; set; } 
     public string Title { get; set; } 

     public PageItemManageEmployeesViewModel(PageItem pageItem) 
     { 

     } 
    } 

    public interface IPageItemViewModel 
    { 
     //these are the properties which every PageItemViewModel needs 
     string IdCode { get; set; } 
     string Title { get; set; } 
    } 

    public abstract class ViewModelBase 
    { 
     protected void OnPropertyChanged(string propertyName) 
     { 
      //this is the INotifyPropertyChanged method which all ViewModels need 
     } 
    } 

    public class PageItem 
    { 
     public string IdCode { get; set; } 
     public string Title { get; set; } 
    } 

    public class PageItems 
    { 
     public static List<PageItem> GetAll() 
     { 
      List<PageItem> pageItems = new List<PageItem>(); 
      pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"}); 
      pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"}); 
      return pageItems; 
     } 
    } 

} 

重构:接口改为抽象类

using System; 
using System.Collections.Generic; 

namespace TestInstantiate838 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      List<PageItem> pageItems = PageItems.GetAll(); 
      List<ViewModelPageItemBase> pageItemViewModels = new List<ViewModelPageItemBase>(); 

      foreach (PageItem pageItem in pageItems) 
      { 
       switch (pageItem.IdCode) 
       { 
        case "manageCustomers": 
         pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem)); 
         break; 
        case "manageEmployees": 
         pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem)); 
         break; 
        default: 
         break; 
       } 
      } 

      foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels) 
      { 
       System.Console.WriteLine("{0}:{1}", pageItemViewModel.IdCode, pageItemViewModel.Title); 
      } 
      Console.ReadLine(); 
     } 
    } 

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase 
    { 
     public PageItemManageCustomersViewModel(PageItem pageItem) 
     { 
      IdCode = pageItem.IdCode; 
      Title = pageItem.Title; 
     } 
    } 

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase 
    { 
     public PageItemManageEmployeesViewModel(PageItem pageItem) 
     { 
      IdCode = pageItem.IdCode; 
      Title = pageItem.Title; 
     } 
    } 

    public abstract class ViewModelPageItemBase : ViewModelBase 
    { 
     //these are the properties which every PageItemViewModel needs 
     public string IdCode { get; set; } 
     public string Title { get; set; } 
    } 

    public abstract class ViewModelBase 
    { 
     protected void OnPropertyChanged(string propertyName) 
     { 
      //this is the INotifyPropertyChanged method which all ViewModels need 
     } 
    } 

    public class PageItem 
    { 
     public string IdCode { get; set; } 
     public string Title { get; set; } 
    } 

    public class PageItems 
    { 
     public static List<PageItem> GetAll() 
     { 
      List<PageItem> pageItems = new List<PageItem>(); 
      pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"}); 
      pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"}); 
      return pageItems; 
     } 
    } 

} 

回答消除开关声明:

感谢戳刺:

string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; 
string viewModelName = assemblyName + ".ViewModels.PageItem" + StringHelpers.ForcePascalNotation(pageItem.IdCode) + "ViewModel"; 
var type = Type.GetType(viewModelName); 
var viewModel = Activator.CreateInstance(type, pageItem) as ViewModelBase; 
AllPageViewModels.Add(viewModel); 

回答

1

一个不是很漂亮,但有效的解决方案是使用约定来摆脱switch语句。这假定您可以更改IdCodes或至少修改大小写以匹配ViewModel。

var type = Type.GetType("PageItem" + pageItem.IdCode + "ViewModel"); 
    var viewModel = Activator.CreateInstance(type) as ViewModelBase; 
    pageItemViewModels.Add(viewModel); 

请注意,您应该在这里添加错误检查,这里有几点失败。然而,最好不要维持不断增长的转换声明。

+0

这只是我寻找的实用方法,我不得不唠叨一下我上面发布的代码,谢谢! – 2009-06-22 16:32:46

2

你可以创建一个BaseViewModel继承,将实现这两​​个属性的类 - 需要那么这可能来自继承你PageItemViewModel类。

+0

感谢这样的作品,我包括上述方案,我还是想摆脱switch语句中,有没有什么办法来实例化动态对象,其继承常见的抽象类? – 2009-06-22 14:30:10

1

正如水稻的建议,我只是创建了一个额外的抽象类,PageViewModelBase,与那些自定义道具:

using System.Collections.Generic; 

namespace TestInstantiate838 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      List<PageItem> pageItems = PageItems.GetAll(); 
      List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>(); 

      foreach (PageItem pageItem in pageItems) 
      { 
       switch (pageItem.IdCode) 
       { 
        case "manageCustomers": 
         pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem)); 
         break; 
        case "manageEmployees": 
         pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem)); 
         break; 
        default: 
         break; 
       } 
      } 
     } 
    } 

    public class PageItemManageCustomersViewModel : PageViewModelBase 
    { 
     public PageItemManageCustomersViewModel(PageItem pageItem) 
     { 

     } 
    } 

    public class PageItemManageEmployeesViewModel : PageViewModelBase 
    { 
     public PageItemManageEmployeesViewModel(PageItem pageItem) 
     { 


     } 
    } 

    public abstract class ViewModelBase 
    { 
     protected void OnPropertyChanged(string propertyName) 
     { 
      //this is the INotifyPropertyChanged method which all ViewModels need 
     } 
    } 

    public abstract class PageViewModelBase : ViewModelBase 
    { 
     //these are the properties which every PageItemViewModel needs 
     public string IdCode { get; set; } 
     public string Title { get; set; } 
    } 

    public class PageItem 
    { 
     public string IdCode { get; set; } 
     public string Title { get; set; } 
    } 

    public class PageItems 
    { 
     public static List<PageItem> GetAll() 
     { 
      List<PageItem> pageItems = new List<PageItem>(); 
      pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"}); 
      pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"}); 
      return pageItems; 
     } 
    } 

} 
+0

交换机中有多少事情可能发生?他们有多大可能被动态添加?对我而言,消除这一点使得通过维护代码阅读你的方式变得困难,同时不能提供很多回报。 – Paddy 2009-06-22 15:51:59

1

为什么你不能把一个GetViewModel()虚方法在你的基地PageItem类返回适当的视图模型?

foreach (PageItem pageItem in pageItems) 
    { 
     pageItemViewModels.Add(pageItem.GetViewModel()); 
    } 

直接看起来像代码味道的东西是“id”属性的用法 - 通常可以用多态性替换它。所以你会用上面的代码替换switch声明。

编辑:

如果您PageItem类并不知道你的视图模型,那么就不能以这种方式实现。基本上,你需要一个你已经拥有的工厂(在某种程度上)。

我通常有一个关系列表(PageItem到ViewModel),在你的情况下这将是一个Dictionary<String, Type>。然后,您可以在初始化期间填充此列表,并在稍后实例化适当的视图模型。

要使用反射来构建此列表,您至少需要通过编程方式知道视图模型支持哪个页面项目。为了这个目的,你可以使用自定义属性来装饰你的类,如:

public class SupportsPageItemAttribute : Attribute 
{ 
    private readonly string _id; 
    public string ID 
    { 
     get { return _id;} 
    } 

    public SupportsPageItemAttribute(string id) 
    { 
     _id = id; 
    } 
} 

,然后使用该属性来定义其PageItem模型可以接受:

[SupportsPageItemAttribute("manageCustomers") 
public class PageItemManageCustomersViewModel 
{ 
    // ... 
} 

,然后你使用反射来获取所有实现IPageItemViewModel的类并检查它们的属性以获得PageItem id字符串。

例如(没有太多错误检查):

Dictionary<String, Type> modelsById = new Dictionary<String, Type>(); 
String viewModelInterface = typeof(IPageItemViewModel).FullName; 

// get the assembly 
Assembly assembly = Assembly.GetAssembly(typeof(IPageItemViewModel)); 

// iterate through all types 
foreach (Type viewModel in assembly.GetTypes()) 
{ 
    // get classes which implement IPageItemViewModel 
    if (viewModel.GetInterface(viewModelInterface) != null) 
    { 
     // get the attribute we're interested in 
     foreach (Attribute att in Attribute.GetCustomAttributes(viewModel)) 
     { 
      if (att is SupportsPageItemAttribute) 
      { 
       // get the page item id 
       String id = (att as SupportsPageItemAttribute).ID; 

       // add to dictionary 
       modelsById.Add(id, viewModel); 
      } 
     } 
    } 
} 

在另一方面,也有可能会考虑的,而不是做讨厌的工作,反映自己各种反转的控制框架。

+0

right,但PageItem类是我的模型类,它应该对MVVM模式中的“ViewModels”一无所知,该模型也可用于例如ASP.NET MVC,不应该返回一个“ViewModel”。 – 2009-06-22 14:33:59

1

一个可能的解决方案是在代码中反转PageItemPageItemViewModel之间的关系。现在,您正在根据PageItem生成PageItemViewModel,但如果您先创建了PageItemViewModel,然后在每个PageItemViewModel的构造函数中创建了相应的PageItem?这消除了对switch的需求,并使事情变得更清洁,因为现在您的视图模型负责模型,而不是模型负责视图模型。

根据您当前的代码示例:

using System; 
using System.Collections.Generic; 

namespace TestInstantiate838 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      List<ViewModelPageItemBase> pageItemViewModels = PageItemViewModels.GetAll(); 

      // No switch needed anymore. Each PageItem's view-model contains its PageItem 
      // which is exposed as property of the view-model. 
      foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels) 
      { 
       System.Console.WriteLine("{0}:{1}", pageItemViewModel.PageItem.IdCode, pageItemViewModel.PageItem.Title); 
      } 
      Console.ReadLine(); 
     } 
    } 

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase 
    { 
     public PageItemManageCustomersViewModel() 
     { 
      PageItem = new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers" }; 
     } 
    } 

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase 
    { 
     public PageItemManageEmployeesViewModel() 
     { 
      PageItem = new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees" }; 
     } 
    } 

    public abstract class ViewModelPageItemBase : ViewModelBase 
    { 
     //The PageItem associated with this view-model 
     public PageItem PageItem { get; protected set; } 
    } 

    public abstract class ViewModelBase 
    { 
     protected void OnPropertyChanged(string propertyName) 
     { 
      //this is the INotifyPropertyChanged method which all ViewModels need 
     } 
    } 

    public class PageItem 
    { 
     public string IdCode { get; set; } 
     public string Title { get; set; } 
    } 

    // Replaces PageItems class 
    public class PageItemViewModels 
    { 
     // Return a list of PageItemViewModel's instead of PageItem's. 
     // Each PageItemViewModel knows how to build it's corresponding PageItem object. 
     public static List<PageItemViewModelBase> GetAll() 
     { 
      List<PageItemViewModelBase> pageItemViewModels = new List<PageItemViewModelBase>(); 
      pageItemViewModels.Add(new PageItemManageCustomersViewModel()); 
      pageItemViewModels.Add(new PageItemManageEmployeesViewModel()); 
      return pageItemViewModels; 
     } 
    } 
}