2009-06-22 24 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; 
     } 
    } 
}