如何模拟多重继承并使用反射来优化此代码?
我有一个 WPF 应用程序,其中 PageItems 是模型对象。
我的主 ViewModel 有一个 PageItemViewModels 的 ObservableCollection,每个模型都从其匹配的 PageItem 模型对象构建自身。
每个 PageItemViewModel 都继承自抽象类 BaseViewModel,以获得 INotifyPropertyChanged 功能。
每个 PageItemViewModel 还实现 IPageItemViewModel 以确保它具有所需的属性。
我最终会有大约 50 页,所以我想消除任何不必要的代码:
- 已解决(见下文):有没有办法让 PageItemViewModel 类继承IdCode 和 Title 这样我就不必在每个类中实现它们? 我无法将它们放入 BaseViewModel 中,因为其他 ViewModel 继承了它,不需要这些属性,并且我无法将它们放入 IPageItemViewModel 中,因为它只是一个接口。 我知道我需要多重继承,而 C# 不支持
- 已解决(见下文):有没有办法摆脱开关 strong> 声明,例如以某种方式使用 reflection 代替?
下面是一个独立控制台应用程序,它演示了我在 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;
}
}
}
消除 Switch 语句的答案:
感谢 Jab:
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);
I've got a WPF application where PageItems are model objects.
My main ViewModel has an ObservableCollection of PageItemViewModels, each one building itself from its matching PageItem model object.
Each PageItemViewModel inherits from the abstract class BaseViewModel in order to get the INotifyPropertyChanged functionality.
Each PageItemViewModel also implements the IPageItemViewModel in order to make sure it has the needed properties.
I will eventually have around 50 pages so I want to eliminate any unnecessary code:
- SOLVED (SEE BELOW): is there a way I can get PageItemViewModel classes to inherit IdCode and Title so I don't have to implement them in each class? I can't put them in BaseViewModel since other ViewModels inherit it which don't need these properties, and I can't put them in IPageItemViewModel since it is only an interface. I understand I need multiple inheritance for this which C# doesn't support
- SOLVED (SEE BELOW): is there a way I can get rid of the switch statement, e.g. somehow use reflection instead?
Below is a stand-alone Console application which demonstrates the code I have in my WPF application:
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;
}
}
}
Refactored: interface changed to abstract class
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;
}
}
}
Answer to eliminating Switch statement:
Thanks Jab:
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);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您是否可以创建一个继承自 BaseViewModel 的类来实现这两个属性 - 然后您需要此属性的 PageItemViewModel 类可以继承该类。
Can you create a class that inherits from BaseViewModel that will implement these two properties - your PageItemViewModel classes that need this could then inherit from that.
一种不太漂亮但有效的解决方案是使用约定来摆脱 switch 语句。 这假设您可以更改 IdCodes 或至少修改大小写以匹配 ViewModel。
请注意,您应该在此处添加错误检查,这里有几个失败点。 然而,这比必须维护不断增长的 switch 语句要好。
One solution that isn't very pretty, but works, would be to use convention to get rid of the switch statement. This assumes you can change the IdCodes or atleast modify the case to match the ViewModel.
Note that you should add error checking here, there are a couple points of failure here. It is, however, better than having to maintain an ever-growing switch statement.
按照 Paddy 的建议,我刚刚创建了一个额外的抽象类 PageViewModelBase,并定义了这些自动属性:
As suggested by Paddy, I just created an additional abstract class, PageViewModelBase, with those auto-props defined:
为什么不能在 PageItem 基类中放置一个
GetViewModel()
虚拟方法来返回适当的视图模型?立即看起来像代码味道的事情是“id”属性的使用 - 这通常可以用多态性代替。 因此,您可以将
switch
语句替换为上面的代码。编辑:
如果您的 PageItem 类对您的视图模型一无所知,则不能以这种方式实现。 基本上,你需要一个工厂,你已经拥有了(在某种程度上)。
我通常有一个关系列表(PageItem 到 ViewModel),在您的情况下是一个
Dictionary
。 然后,您可以在初始化期间填充此列表,并在稍后实例化正确的视图模型。要使用反射来构建此列表,您至少需要以编程方式了解视图模型支持哪个页面项。 为此,您可以使用自定义属性来装饰您的类,例如:
然后使用该属性来定义您的模型可以接受哪个 PageItem:
然后,您使用反射来获取实现 IPageItemViewModel 的所有类并检查它们的属性以获取PageItem id 字符串。
例如(没有太多的错误检查):
另一方面,您可以考虑各种控制反转框架,而不是自己做讨厌的反射工作。
Why can't you put a
GetViewModel()
virtual method in your base PageItem class which returns the appropriate view model?The thing that immediately looks like a code smell is the usage of "id" properties - this can usually be replaced with polymorphism. So you would replace the
switch
statement with the code above.Edit:
If your PageItem class knows nothing about your view model, then it can not be implemented this way. Basically, you need a factory, which you already have (in a way).
I usually have a list of relations (PageItem to ViewModel), which would in your case be a
Dictionary<String, Type>
. Then you can fill this list during initialization and have the proper view model instantiated later.To use reflection to build this list, you at least need to know programmatically which page item is supported by a view model. For that purpose, you could use a custom attribute to decorate you class, eg.:
And then use that attribute to define which PageItem your model can accept:
And then, you use reflection to get all classes implementing IPageItemViewModel and check their attributes to get the PageItem id string.
For example (without much error-checking):
On the other hand, there are various Inversion-of-control frameworks you might consider instead of doing the nasty reflection work yourself.
一种可能的解决方案是反转代码中
PageItem
和PageItemViewModel
之间的关系。 现在,您正在基于PageItem
生成PageItemViewModel
,但是如果您先创建PageItemViewModel
,然后在每个中创建,会怎么样? PageItemViewModel
的构造函数中,您创建了适当的PageItem
吗? 这消除了对switch
的需要,并使事情变得更清晰,因为现在你的视图模型负责模型,而不是模型负责视图模型。基于您当前代码的示例:
One possible solution is to reverse the relationship between
PageItem
andPageItemViewModel
in your code. Right now, you are generating aPageItemViewModel
based on aPageItem
, but what if you created thePageItemViewModel
s first and then in eachPageItemViewModel
's constructor, you created the appropriatePageItem
? This eliminates the need for theswitch
and makes things cleaner, because now your view-model is responsible for the model, instead of the model being responsible for the view-model.An example based on your current code: