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

C# 确定继承接口类类型的最佳方法是什么?,c#,interface,C#,Interface,在我的应用程序中,我使用criterias。我有一个基本标准接口和从该基本接口继承的其他接口: ICriteria | | ---------------------- | | ITextCriteria IChoices 我的问题是,这是最好的方式吗?还是有更好的方法来实现这一点 很有可能会有更多基于ICriteria的接口 编辑:

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

ICriteria | | ---------------------- | | ITextCriteria IChoices 我的问题是,这是最好的方式吗?还是有更好的方法来实现这一点

很有可能会有更多基于ICriteria的接口

编辑:

我有两种类型的控件,我想动态添加到我的应用程序中。一个控件是文本框,另一个是单选按钮

对于单选按钮,用户可以定义选项。定义选项后,用户必须选择其中一个选项,并且所选选项必须保存在数据库中,该数据库稍后用于执行搜索操作。因此,当单击Save按钮时,我必须确定所选的类型radio或text,如果是radio,则保存答案

对于文本框,这没有任何答案的可能性。因此,它有一个不同的接口

我希望我现在能说清楚一点。这是另一个相关的问题:

编辑二:

这就是我的方法SaveMultipleEchoiceValues的样子:

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,该界面将为案例中的视图建模,并显示ICriteria。表单需要根据条件实现的确切接口进行一些处理,因此为代码中存在的每个接口添加一个带有一个重载的方法。不要为ICriteria本身添加重载。[1]

您的表单将实现此接口,并为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重载。这就是重定向魔法发生的地方:我们将使用多态性让编译器发现对象的实际类型,然后在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.ProcessCriteriathis。反过来,这将迫使您在ICriteriaView和MyForm中实现适当的ProcessCriteria重载

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

编译器将不允许您添加新的ICriteria实现,而不指定该实现应如何与ICriteriaView交互。 从源代码中很容易发现MyView在读取多回音代码时对例如IChoices的具体操作。代码的结构会自动引导您找到MyForm.SaveMultipleChiceValues。 注:

[1] 选择是否为ICriteria本身添加重载实际上是一种折衷:

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

class MultipleChoice : IChoices {
    public PerformProcessingOn(ICriteriaView view) {
        view.ProcessCriteria(this);
    }
}
将始终成功编译,因为即使没有ICriteriaView.ProcessCriteriaIChoices重载,编译器仍可以使用ICriteriaView.ProcessCriteriaICriteria重载

这意味着,当添加新的ICriteria子接口实现时,编译器将不再强制您检查ICriteriaView.ProcessCriteriaICriteria的实现是否真的适合您的新实现

如果不添加,则在编写view.ProcessCriteriathis时;编译器将强制您检查并相应地更新ICriteriaView和MyForm

在这种情况下,根据您提供的信息,我相信最后一个选择是正确的

[2] 如上所示,MultipleChice和SimpleInput内部的ICriteria.PerformProcessing的实现看起来完全相同。如果这两个类有一个在实践中非常可能的公共基,那么您可能会尝试将重复的代码移动到该基中。不要那样做;这将导致解决方案中断

棘手的部分是当你查看时,在MultipleChice内部;编译器可以推断出它的静态类型是IChoices
-这就是重定向发生的地方!如果将对ProcessCriteria的调用移动到假设的基类CriteriaBase:ICriteria中,则该类型将成为ICriteria,并且将调用分派到相应的ICriteriaView.ProcessCriteria重载将不再工作。

这看起来像是多态性的一个主要示例

与其尝试在ICriteria实现上进行类型切换,不如在ICriteria中添加一个方法,或者在所有ICriteria实现的某个公共基类中添加一个虚拟方法,然后调用它

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

更新:

下面是一个完整的解决方案,包含您发布的代码:

创建一个新界面ICriteriaView,该界面将为案例中的视图建模,并显示ICriteria。表单需要根据条件实现的确切接口进行一些处理,因此为代码中存在的每个接口添加一个带有一个重载的方法。不要为ICriteria本身添加重载。[1]

您的表单将实现此接口,并为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重载。这就是重定向魔法发生的地方:我们将使用多态性让编译器发现对象的实际类型,然后在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.ProcessCriteriathis。反过来,这将迫使您在ICriteriaView和MyForm中实现适当的ProcessCriteria重载

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

编译器将不允许您添加新的ICriteria实现,而不指定该实现应如何与ICriteriaView交互。 从源代码中很容易发现MyView在读取多回音代码时对例如IChoices的具体操作。代码的结构会自动引导您找到MyForm.SaveMultipleChiceValues。 注:

[1] 选择是否为ICriteria本身添加重载实际上是一种折衷:

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

class MultipleChoice : IChoices {
    public PerformProcessingOn(ICriteriaView view) {
        view.ProcessCriteria(this);
    }
}
将始终成功编译,因为即使没有ICriteriaView.ProcessCriteriaIChoices重载,编译器仍可以使用ICriteriaView.ProcessCriteriaICriteria重载

这意味着,当添加新的ICriteria子接口实现时,编译器将不再强制您检查ICriteriaView.ProcessCriteriaICriteria的实现是否真的适合您的新实现

如果不添加,则在编写view.ProcessCriteriathis时;编译器将强制您检查并相应地更新ICriteriaView和MyForm

在这种情况下,根据您提供的信息,我相信最后一个选择是正确的

[2] 如上所示,MultipleChice和SimpleInput内部的ICriteria.PerformProcessing的实现看起来完全相同。如果这两个类有一个在实践中非常可能的公共基,那么您可能会尝试将重复的代码移动到该基中。不要那样做;这将导致解决方案中断


棘手的部分是当你查看时,在MultipleChice内部;编译器可以推断这是IChoices的静态类型-这是重定向发生的地方!如果将对ProcessCriteria的调用移动到假设的基类CriteriaBase:ICriteria中,则该类型将变为ICriteria,并且将调用分派到相应的ICriteriaView.ProcessCriteria重载将不再工作。

这不是最好的方法。如果您根据对象的类型执行不同的操作,那么您可能应该使用多态性,原因有很多

您如何使用多态性取决于您根据所使用的不同类型的ICriteria实际需要做什么。如果您只需要获取包含其所有成员的字符串,您可以轻松地向ICriteria添加一个方法,并将责任交给类本身,而不是依赖于它的代码。这样可以减少重复,将代码放在合理的位置,并确保您不会忘记添加co 这是一种新型的ICriteria


如果您向我们提供更多关于您希望不同类型的人如何对待/表现的信息,我们可能会向您提供更具体的建议:这不是最好的办法。如果您根据对象的类型执行不同的操作,那么您可能应该使用多态性,原因有很多

您如何使用多态性取决于您根据所使用的不同类型的ICriteria实际需要做什么。如果您只需要获取包含其所有成员的字符串,您可以轻松地向ICriteria添加一个方法,并将责任交给类本身,而不是依赖于它的代码。这样可以减少重复,将代码放在逻辑位置,并确保您不会忘记为新类型的ICriteria添加代码

如果您向我们提供更多关于您希望不同类型的人如何对待/表现的信息,我们可能会向您提供更具体的建议:你可以这样做:

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;
    }
}
但是多态性可能是你最好的选择。

你可以这样做:

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;
    }
}

但是多态性可能是你最好的选择。

这里有一个长期解决方案,可以解决不断扩大的标准列表,而不必添加更多的if/then/else

虽然此代码对于不习惯以这种方式进行设计的人来说很复杂,但它允许您保持处理条件的方法不变,并且只注册新委托来处理其他条件

其思想是创建一个类型对象的映射,其中包含要执行的委托。然后,您可以注册新的委托,以便在生成它们时基于新类型执行它们

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");
        }
    }
}

这里有一个长期解决方案,可以解决不断扩大的准则列表,而不必添加更多的if/then/else

虽然此代码对于不习惯以这种方式进行设计的人来说很复杂,但它允许您保持处理条件的方法不变,并且只注册新委托来处理其他条件

其思想是创建一个类型对象的映射,其中包含要执行的委托。然后,您可以注册新的委托,以便在生成它们时基于新类型执行它们

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");
        }
    }
}

你想达到什么目标?如果你需要以这种方式找出类型,你可能做错了什么。你想达到什么目的?如果您需要以这种方式查找类型,则可能是做错了什么。如果我将方法SaveMultipleChiceValues添加到ICriteria,那么当条件为textbox时,我也可以调用此方法,但textbox没有答案。所以这是错误的。这就是为什么我创建了一个继承自ICriteria@Martijn:我加了一个例子,看一看。@Jon:这个解决方案把我的头脑搞乱了,呵呵。这对我来说是一种新的方法。我对这种解决方案没有太多经验。不过,我觉得这很有趣。所以,谢谢你。但我不能解决这个难题。我知道你为我做了很多,但我不知道userInterfaceHelper从var userInterfaceHelper=…;//中得到了什么取决于您的应用程序及其初始化方式。@Martijn:userInterfaceHelper将执行SaveMultipleChiceValues在代码中执行的任何操作。把你的代码移到那边去。@Jon:我现在觉得很愚蠢:$我不明白。savemultipleechoicevalues是一种使用答案可能性在网格中循环的方法。我是否必须执行var userInterfaceHelper=savemultipleechoicevaluescriteria;?很抱歉,我不明白。如果我将方法savemultipleechoicevalues添加到ICriteria,那么当条件是文本框时,我也可以调用此方法,但是文本框没有答案的可能性。所以这是错误的。这就是为什么我创建了一个继承自ICriteria@Martijn:我加了一个例子,看一看。@Jon:这个解决方案把我的头脑搞乱了,呵呵。这对我来说是一种新的方法。我对这种解决方案没有太多经验。不过,我觉得这很有趣。所以,谢谢你。但我不能解决这个难题。我知道你为我做了很多,但我不知道userInterfaceHelper从var userInterfaceHelper=…;//中得到了什么取决于您的应用程序及其初始化方式。@Martijn:userInterfaceHelper将执行SaveMultipleChiceValues在代码中执行的任何操作。把你的代码移到那边去。@Jon:我现在觉得很愚蠢:$我不明白。savemultipleechoicevalues是一种使用答案可能性在网格中循环的方法。我是否必须执行var userInterfaceHelper=savemultipleechoicevaluescriteria;?对不起,我不明白..在我可以给别人留言之前,我会给自己的帖子添加评论。我不建议在ICriteria中添加方法。ICriteria似乎是一个包含数据的模型类型对象,但与对critera采取的操作无关。如果向ICriteria添加了与对其执行的操作相关的代码,则开发人员已将ICriteria锁定为仅在其特定世界中使用。ICriteria可以设计成可以在其他地方使用,并且可以根据需要进行不同的操作
在程序中使用ICriteria的位置。你真的不想把控制器代码放在模型代码中。我认为你的推理有一些错误:向ICriteria添加方法与向ICriteria添加代码不同,因为代码==逻辑,但方法可能只是一种机制,不包含逻辑。b正如您在我的回答中所看到的,没有必要将iCiteria与具体实施细节结合起来;通用编程实践可以很好地实现可扩展性。最后,你提出的解决方案有一个严重的缺点:DIY多态性非常脆弱。在我可以向其他人发表评论之前,请在我自己的帖子中添加评论。我不建议在ICriteria中添加方法。ICriteria似乎是一个包含数据的模型类型对象,但与对critera采取的操作无关。如果向ICriteria添加了与对其执行的操作相关的代码,则开发人员已将ICriteria锁定为仅在其特定世界中使用。ICriteria可以设计成可以在其他地方使用,并且可以根据程序中使用ICriteria的位置执行不同的操作。你真的不想把控制器代码放在模型代码中。我认为你的推理有一些错误:向ICriteria添加方法与向ICriteria添加代码不同,因为代码==逻辑,但方法可能只是一种机制,不包含逻辑。b正如您在我的回答中所看到的,没有必要将iCiteria与具体实施细节结合起来;通用编程实践可以很好地实现可扩展性。最后,您提出的解决方案有一个严重的缺点:DIY多态性非常脆弱。