在我的应用程序中,我使用标准。我有一个基本Criteria接口和其他继承自此基本接口的接口:
ICriteria | | ---------------------- | | ITextCriteria IChoices
我想知道的是,了解班级类型的最佳方法是什么?
在我的代码中,我有一个下拉框,基于此我必须确定类型:
// Get selected criteria
var selectedCriteria = cmbType.SelectedItem as ICriteria;
if (selectedCriteria is IChoices)
{
//selectedCriteria = cmbType.SelectedItem as IChoices; Doesn't work
IChoices criteria = selectedCriteria as IChoices;//cmbType.SelectedItem as IChoices;
SaveMultipleChoiceValues(criteria);
//_category.AddCriteria(criteria);
}
else
{
//ICriteria criteria = selectedCriteria; //cmbType.SelectedItem as ICriteria;
if (selectedCriteria.GetCriteriaType() == CriteriaTypes.None)
{
return;
}
//_category.AddCriteria(criteria);
}
_category.AddCriteria(selectedCriteria);
selectedCriteria.LabelText = txtLabeltext.Text;
this.Close();
我的问题是,这是最好的方法吗?或者有更好的方法来实现这一目标吗?
基于ICriteria的接口越来越多的机会很大。
编辑:
我有两种类型的控件,我想动态添加到我的应用程序中。一个控件是文本框,另一个是单选按钮。
对于单选按钮,用户可以定义选项。定义选项后,用户必须选择其中一个选项,并且所选选项必须保存在数据库中(稍后用于执行搜索操作)。因此,当单击“保存”按钮时,我必须确定所选类型(无线电或文本)并保存答案可能性(如果它是收音机)。
对于文本框,这没有任何答案可能性。因此,它有一个不同的界面。
我希望我现在能说清楚一点。这是另一个相关的问题:C# How to implement interface where concrete classes differs?
编辑II:
这就是我的方法SaveMultipleChoiceValues
的样子:
private void SaveMultipleChoiceValues(IChoices criteria)
{
foreach (DataGridViewRow row in dgvCriteriaControls.Rows)
{
if (row == dgvCriteriaControls.Rows[dgvCriteriaControls.Rows.Count - 1])
continue;
//multipleChoice.AddChoice(row.Cells["Name"].Value.ToString());
string choice = row.Cells["Name"].Value.ToString();
criteria.AddChoice(choice);
}
}
答案 0 :(得分:4)
这看起来像是多态性的一个主要例子。
不是试图在ICriteria
实现上进行类型切换,而是为什么不向ICriteria
添加方法(或者可能将虚方法添加到所有{{1}的公共基类中实现),只是调用它?
显然,此方法的实现需要访问不属于ICriteria
实例的对象,但这是您可以根据场景的具体情况使用其他设计模式解决的问题。
<强>更新强>
这是一个完整的解决方案,包含您发布的代码:
创建一个新界面ICriteria
,为视图(在您的情况下为ICriteriaView
)建模,其中显示Form
。表单需要根据条件实现的确切接口进行一些处理,因此为代码中存在的每个接口添加一个带有一个重载的方法。 不要为ICriteria
本身添加重载。 [1]
ICriteria
您的表单将实现此接口,提供适用于interface ICriteriaView {
void ProcessCriteria(IChoices criteria);
void ProcessCriteria(ITextCriteria criteria);
}
每个子类型的处理的方法:
ICriteria
class MyForm : ICriteriaView {
public void ProcessCriteria(IChoices criteria) {
this.SaveMultipleChoiceValues(criteria);
}
public void ProcessCriteria(ITextCriteria criteria) {
// do nothing
}
private void SaveMultipleChoiceValues(IChoices criteria)
{
foreach (DataGridViewRow row in dgvCriteriaControls.Rows)
{
if (row == dgvCriteriaControls.Rows[dgvCriteriaControls.Rows.Count - 1])
continue;
//multipleChoice.AddChoice(row.Cells["Name"].Value.ToString());
string choice = row.Cells["Name"].Value.ToString();
criteria.AddChoice(choice);
}
}
}
的每个实现都需要实现一个方法,为其类型调用适当的ICriteria
重载。这就是“重定向魔法”发生的地方:我们将使用多态来让编译器“发现”我们对象的ICriteriaView
的实际类型,然后使用ICriteria
上的方法重载来访问相应的代码。
ICriteriaView.ProcessCriteria
这是发生适当重载的地方:
interface ICriteria {
void PerformProcessingOn(ICriteriaView view);
}
interface IChoices : ICriteria {
}
interface ITextCriteria : ICriteria {
}
然后,您的代码将执行:
class MultipleChoice : IChoices {
public PerformProcessingOn(ICriteriaView view) {
view.ProcessCriteria(this);
}
}
class SimpleInput : ITextCriteria {
public PerformProcessingOn(ICriteriaView view) {
view.ProcessCriteria(this);
}
}
<强>维护:强>
每当您添加新的// Get selected criteria
var selectedCriteria = cmbType.SelectedItem as ICriteria;
// Here's where polymorphism kicks in
selectedCriteria.PerformProcessingOn(this);
// Finally, code that runs the same for all objects
_category.AddCriteria(selectedCriteria);
selectedCriteria.LabelText = txtLabeltext.Text;
this.Close();
子接口实现时,ICriteria
的定义将强制您在其上实施ICriteria
方法。在该方法中,您所能做的就是致电PerformProcessingOn
。反过来,这会迫使您在view.ProcessCriteria(this)
和ProcessCriteria
中实施适当的ICriteriaView
重载。
因此,我们实现了两个重要目标:
MyForm
实现,而不准确指定该实现应如何与ICriteria
交互。ICriteriaView
所做的事情。阅读MyView
的代码时IChoices
。代码的结构会引导您MultipleChoice
“自动”。备注:强>
[1] 为MyForm.SaveMultipleChoiceValues
本身添加重载的选择实际上是一种权衡:
如果你做添加一个,那么代码如下:
ICriteria
将始终成功编译,因为即使没有class MultipleChoice : IChoices {
public PerformProcessingOn(ICriteriaView view) {
view.ProcessCriteria(this);
}
}
重载,仍然会有编译器可以使用的ICriteriaView.ProcessCriteria(IChoices)
重载。
这意味着,在添加新的ICriteriaView.ProcessCriteria(ICriteria)
子接口实现时,编译器将不再强制您检查ICriteria
的实现是否确实你的新实施是正确的。
如果您不添加一个,那么当您编写ICriteriaView.ProcessCriteria(ICriteria)
时,编译器将强制您去检查(和更新){ {1}}和view.ProcessCriteria(this);
相应地。
在这种情况下,根据您提供的信息,我认为适当的选择将是最后一个。
[2] 如您所见,ICriteriaView
和MyForm
内ICriteria.PerformProcessingOn
的实现看起来完全相同。如果这两个类有一个共同的基础(实际上很可能),你可能会想把“重复”的代码移到那个基础中。 不要那样做;它会导致解决方案中断。
棘手的部分是在MultipleChoice
内,当你SimpleInput
时,编译器可以推断MultipleChoice
的静态类型是view.ProcessCriteria(this);
- 这是重定向的地方发生!如果您将调用移至假设基类this
内的IChoices
,则ProcessCriteria
的类型将变为CriteriaBase : ICriteria
,并将调用发送到相应的{{1}重载将不再有效。
答案 1 :(得分:1)
你可以这样做:
var selectedCriteria = cmbType.SelectedItem as ICriteria;
if (typeof(IChoices).IsAssignableFrom(selectedCriteria.GetType()))
{
IChoices criteria = selectedCriteria as IChoices;
SaveMultipleChoiceValues(criteria);
}
else if(typeof(ITextCriteria).IsAssignableFrom(selectedCriteria.GetType()))
{
if (selectedCriteria.GetCriteriaType() == CriteriaTypes.None)
{
return;
}
}
但多态可能是你最好的选择。
答案 2 :(得分:0)
这不是最好的方法。如果您根据对象的类型执行不同的操作,则应该使用多态而不是出于多种原因。
如何使用多态性取决于您实际需要根据所使用的不同类型的ICriteria完成的操作。如果您只需要获取包含其所有成员的字符串,则可以轻松地向ICriteria添加方法,并将责任交给类本身而不是依赖于它的代码。这减少了重复,将代码放在逻辑位置,并确保您不要忘记为新类型的ICriteria添加代码。
如果您向我们提供有关您希望如何处理不同类型/行为的更多信息,我们可能会为您提供更具体的建议。 :d
答案 3 :(得分:0)
这是一个不断扩展的critera列表的长期解决方案,无需添加更多if / then / else。
虽然这个代码对于那些不习惯以这种方式进行设计的人来说很复杂,但是它允许你让你的方法处理标准相同,只需注册新的代表来处理其他标准。
我们的想法是创建一个Type
个对象的映射,这些对象包含要在其中执行的委托。然后,您可以在生成它们时根据新的Type
注册新的委托。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Stackoverflow_4527626
{
delegate void CriteraDelegate(params object[] args);
class CriteraManager
{
private Dictionary<Type, CriteraDelegate> criterian = new Dictionary<Type, CriteraDelegate>();
public void RegisterCritera(Type type, CriteraDelegate del)
{
criterian[type] = del;
}
public void Execute(Object criteria, params object[] args)
{
Type type = criteria.GetType();
/// Check to see if the specific type
/// is in the list.
if (criterian.ContainsKey(type))
{
criterian[type](args);
}
/// If it isn't perform a more exhaustive search for
/// any sub types.
else
{
foreach (Type keyType in criterian.Keys)
{
if (keyType.IsAssignableFrom(type))
{
criterian[keyType](args);
return;
}
}
throw new ArgumentException("A delegate for Type " + type + " does not exist.");
}
}
}
interface InterfaceA { }
interface InterfaceB1 : InterfaceA { }
interface InterfaceB2 : InterfaceA { }
interface InterfaceC { }
class ClassB1 : InterfaceB1 { }
class ClassB2 : InterfaceB2 { }
class ClassC : InterfaceC { }
class Program
{
static void ExecuteCritera1(params object[] args)
{
Console.WriteLine("ExecuteCritera1:");
foreach (object arg in args)
Console.WriteLine(arg);
}
static void ExecuteCritera2(params object[] args)
{
Console.WriteLine("ExecuteCritera2:");
foreach (object arg in args)
Console.WriteLine(arg);
}
static void Main(string[] args)
{
CriteraDelegate exampleDelegate1 = new CriteraDelegate(ExecuteCritera1);
CriteraDelegate exampleDelegate2 = new CriteraDelegate(ExecuteCritera2);
CriteraManager manager = new CriteraManager();
manager.RegisterCritera(typeof(InterfaceB1), exampleDelegate2);
manager.RegisterCritera(typeof(InterfaceB2), exampleDelegate2);
manager.RegisterCritera(typeof(InterfaceC), exampleDelegate1);
ClassB1 b1 = new ClassB1();
ClassB2 b2 = new ClassB2();
ClassC c = new ClassC();
manager.Execute(b1, "Should execute delegate 2");
manager.Execute(b2, "Should execute delegate 2");
manager.Execute(c, "Should execute delegate 1");
}
}
}