c# - C#确定继承接口(interface)类的最佳方法是什么?

标签 c# interface

在我的应用程序中,我使用条件。我有一个基本的Criteria接口和其他从该基本接口继承的接口:

               ICriteria
                  |
                  |
        ----------------------
         |                  |
    ITextCriteria        IChoices

What I'd like to know is, what is the best way to know what Type the class is?

In my code I have a dropdown box and based on that I have to determine the type:

// 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?

编辑二:

这是我的方法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);
    }
}

最佳答案

这看起来是多态性的一个典型例子。

而不是尝试在ICriteria实现上进行类型切换,为什么不向ICriteria添加方法(或者可能将虚拟方法添加到所有ICriteria实现的某些通用基类中),而只是调用它?

显然,此方法的实现将需要访问不属于您的ICriteria实例的对象,但这是一个问题,您可以根据具体情况使用其他设计模式来解决。

更新:

这是一个完整的解决方案,其中包含您发布的代码:

创建一个新界面ICriteriaView,该界面对显示Form的视图(在您的情况下为ICriteria)进行建模。表单需要根据标准实现的确切接口进行一些处理,因此为代码中存在的每个接口添加一个带有一个重载的方法。不要为ICriteria本身添加重载。 [1]

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)。反过来,这将迫使您在ProcessCriteriaICriteriaView中实现适当的MyForm重载。

结果,我们实现了两个重要目标:


编译器不允许您添加新的ICriteria实现,而无需确切指定该实现应如何与ICriteriaView交互。
很容易从源代码中准确地发现MyView的作用,例如读取IChoices的代码时为MultipleChoice。代码的结构使您“自动”进入MyForm.SaveMultipleChoiceValues


笔记:

[1]是否为ICriteria本身添加重载的选择实际上是一个权衡:


如果确实添加一个,则代码如下:

class MultipleChoice : IChoices {
    public PerformProcessingOn(ICriteriaView view) {
        view.ProcessCriteria(this);
    }
}


会始终成功编译,因为即使没有ICriteriaView.ProcessCriteria(IChoices)重载,编译器仍然可以使用ICriteriaView.ProcessCriteria(ICriteria)重载。

这意味着,当添加新的ICriteria子接口实现时,编译器将不再迫使您检查ICriteriaView.ProcessCriteria(ICriteria)的实现是否确实为您的新实现做了正确的事情。
如果不加,则在编写view.ProcessCriteria(this);时,编译器将强制您相应地检查(并更新)ICriteriaViewMyForm


在这种情况下,并根据您提供的信息,我认为适当的选择将是最后一个选择。

[2]如上所示,在ICriteria.PerformProcessingOnMultipleChoiceSimpleInput的实现看起来完全相同。如果这两个类具有相同的基础(实际上很可能),那么您可能会想将“重复的”代码移入该基础。不要那样做;它将导致解决方案破裂。

棘手的是,在MultipleChoice内部,当您执行view.ProcessCriteria(this);时,编译器可以推断this的静态类型为IChoices -这就是重定向发生的地方!如果将调用移至假设的基类ProcessCriteria中的CriteriaBase : ICriteria,则this的类型将变为ICriteria,并且将调用分派到适当的ICriteriaView.ProcessCriteria重载将不再起作用。

关于c# - C#确定继承接口(interface)类的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4527626/

相关文章:

c# - 如何列出省略属性访问器的接口(interface)方法

c# - 用数据库查询结果填充列表框

c# - 如何让 C# 文本框自动完成以根据包含而不是开头提出建议?

c# - 我怎样才能避免双重类型转换?

java - 是否有可能有一个具有抽象和完整方法的 Java 接口(interface)?

Java 接口(interface) Start/Stop 已经存在?

oop - 接口(interface)与抽象类(一般 OO)

c# - Server.MapPath - 不允许加载本地资源错误

c# - 错误 GridView 'GridView1' 触发了未处理的事件排序

c# - 如果发生错误,using 语句是否会回滚数据库事务?