C#确定继承接口类的类型的最佳方法是什么?

时间:2010-12-24 17:50:14

标签: c# interface

在我的应用程序中,我使用标准。我有一个基本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);
    }
}

4 个答案:

答案 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重载。

因此,我们实现了两个重要目标:

  1. 编译器不允许您添加新的MyForm实现,而不准确指定该实现应如何与ICriteria交互。
  2. 很容易从源代码中发现完全 ICriteriaView所做的事情。阅读MyView的代码时IChoices。代码的结构会引导您MultipleChoice“自动”。
  3. 备注:

    [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] 如您所见,ICriteriaViewMyFormICriteria.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");
        }
    }
}