简体   繁体   English

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

[英]C# What is the best way to determine the type of an inherited interface class?

In my application I work with criterias. 在我的应用程序中,我使用标准。 I have one base Criteria interface and and other interfaces who inherits from this base 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();

My question is, is this the best way? 我的问题是,这是最好的方法吗? Or is there a better way to achieve this? 还是有更好的方法来实现这一目标?

The chance is big that there are coming more interfaces based on ICriteria. 很有可能会有更多基于ICriteria的接口。

EDIT: 编辑:

I have 2 types of controls which I want to add dynamically to my application. 我有两种类型的控件,我想动态添加到我的应用程序中。 One control is a textbox and the other is a radio button. 一个控件是一个文本框,另一个控件是一个单选按钮。

For a radio button the user can define the options. 用户可以为单选按钮定义选项。 When the options are defined, the user must choose one of the options and the chosen option must be saved in the database (this is later used to perform search operations). 定义选项后,用户必须选择一个选项,并且所选的选项必须保存在数据库中(以后将用于执行搜索操作)。 So, when the Save button is clicked, I have to determine the chosen type (radio or text) and save the answer possibilities (if it is a radio). 因此,单击“保存”按钮时,我必须确定所选的类型(单选或文本)并保存答案的可能性(如果是单选)。

For a textbox, this doesn't have any answer possibilities. 对于文本框,这没有任何可能的答案。 For that reason it has a different interface. 因此,它具有不同的界面。

I hope I make it a little bit clearer now. 我希望现在可以使它更加清晰。 Here is another question which is related: C# How to implement interface where concrete classes differs? 这是另一个相关的问题: C#如何在具体类不同的地方实现接口?

EDIT II: 编辑二:

This is how my method SaveMultipleChoiceValues looks like: 这是我的方法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);
    }
}

This looks like a prime example for polymorphism. 这看起来是多态性的一个典型例子。

Instead of trying to do a type switch on your ICriteria implementation, why don't you add a method to ICriteria (or possibly a virtual method to some common base class of all ICriteria implementations), and just call that? 而不是试图做一个类型的开关在你ICriteria实现,你为什么不添加一个方法ICriteria (或者可能是一个虚拟的方法,一些公共基类所有ICriteria实现),并调用了吗?

Obviously the implementation of this method would need access to objects that do not belong in your ICriteria instances, but that is a problem you can solve using other design patterns according to the specifics of your scenario. 显然,此方法的实现将需要访问不属于您的ICriteria实例的对象,但这是一个问题,您可以根据方案的具体情况使用其他设计模式来解决。

Update: 更新:

Here's a complete solution, incorporating the code you posted: 这是一个完整的解决方案,其中包含您发布的代码:

Create a new interface ICriteriaView which models the view (in your case a Form ) where ICriteria are displayed. 创建一个新的接口ICriteriaView ,以对显示ICriteriaView的视图(在您的情况下为Form )进行ICriteria The form needs to do some processing depending on the exact interface that criteria implement, so add a method with one overload for each interface that exists in your code. 表单需要根据标准实现的确切接口进行一些处理,因此为代码中存在的每个接口添加一个带有一个重载的方法。 Do not add an overload for ICriteria itself. 不要为ICriteria本身添加重载。 [1] [1]

interface ICriteriaView {
    void ProcessCriteria(IChoices criteria);
    void ProcessCriteria(ITextCriteria criteria);
}

Your form will implement this interface, providing methods where suitable processing for each subtype of ICriteria will occur: 您的表单将实现此接口,并提供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);
        }
    }

}

Each implementation of ICriteria will need to implement a method which calls the appropriate ICriteriaView overload for its type. 每个ICriteria实现都需要实现一个方法,该方法为其类型调用适当的ICriteriaView重载。 This is where the "redirection magic" happens: we will use polymorphism to get the compiler to "discover" the actual type of ICriteria our object is, and then use method overloading on ICriteriaView.ProcessCriteria to access the appropriate code. 这就是发生“重定向魔术”的地方:我们将使用多态性来使编译器“发现”对象的实际ICriteria类型,然后在ICriteriaView.ProcessCriteria上使用方法重载来访问适当的代码。

interface ICriteria {
    void PerformProcessingOn(ICriteriaView view);
}

interface IChoices : ICriteria {
}

interface ITextCriteria : ICriteria {
}

And this is where the dispatch to the appropriate overload happens: 这是向适当的重载进行调度的地方:

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

class SimpleInput : ITextCriteria {
    public PerformProcessingOn(ICriteriaView view) {
        view.ProcessCriteria(this);
    }
}

Then, your code would do: 然后,您的代码将执行以下操作:

// 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();

Maintenance: 保养:

Whenever you add a new ICriteria sub-interface implementation, the definition of ICriteria will force you to implement the PerformProcessingOn method on it. 每当您添加新的ICriteria子接口实现时, ICriteria的定义就会强制您对其执行PerformProcessingOn方法。 Inside that method, all you can do really is call view.ProcessCriteria(this) . 在该方法中,您真正可以做的就是调用view.ProcessCriteria(this) In turn, this will force you to implement an appropriate ProcessCriteria overload in ICriteriaView and MyForm . 反过来,这将迫使您在ICriteriaViewMyForm实现适当的ProcessCriteria重载。

As a result, we have achieved two important objectives: 结果,我们实现了两个重要目标:

  1. The compiler will not allow you to add a new ICriteria implementation without specifying exactly how that implementation should interact with ICriteriaView . 编译器将不允许您添加新的ICriteria实现,而无需确切指定该实现应如何与ICriteriaView交互。
  2. It is easy to discover from source code exactly what MyView does with eg IChoices when reading the code for MultipleChoice . 在读取MultipleChoice的代码时,很容易从源代码中准确地发现MyViewIChoices的工作。 The structure of the code leads you to MyForm.SaveMultipleChoiceValues "automatically". 代码的结构使您“自动”进入MyForm.SaveMultipleChoiceValues

Notes: 笔记:

[1] The choice of adding an overload for ICriteria itself or not is really a tradeoff: [1]选择是否为ICriteria本身添加重载确实是一个权衡:

  • If you do add one, then code like this: 如果确实添加一个,则代码如下:

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

    will compile successfully always, because even if there is no ICriteriaView.ProcessCriteria(IChoices) overload there will still be the ICriteriaView.ProcessCriteria(ICriteria) overload that the compiler can use. 将编译成功永远,因为即使没有ICriteriaView.ProcessCriteria(IChoices)超载还是会有的ICriteriaView.ProcessCriteria(ICriteria)超载,编译器可以使用。

    This means that, when adding a new ICriteria sub-interface implementation, the compiler will no longer force you to go check if the implementation of ICriteriaView.ProcessCriteria(ICriteria) really does the right thing for your new implementation. 这意味着,当添加新的ICriteria子接口实现时,编译器将不再迫使您检查ICriteriaView.ProcessCriteria(ICriteria)的实现是否确实为您的新实现做了正确的事情。

  • If you do not add one, then the moment you write view.ProcessCriteria(this); 如果您添加一个,那么在您编写view.ProcessCriteria(this);的那一刻view.ProcessCriteria(this); the compiler will force you to go check (and update) ICriteriaView and MyForm accordingly. 编译器将迫使您相应地检查(和更新) ICriteriaViewMyForm

In this scenario, and with the information you have provided, I believe that the appropriate choice would be the last one. 在这种情况下,并根据您提供的信息,我认为适当的选择将是最后一个选择。

[2] As you can see above, the implementation of ICriteria.PerformProcessingOn inside MultipleChoice and SimpleInput looks exactly the same. [2]正如你可以在上面看到,实施ICriteria.PerformProcessingOnMultipleChoiceSimpleInput长得一模一样。 If these two classes have a common base (which is quite possible in practice), you might be tempted to move the "duplicated" code into that base. 如果这两个类具有相同的基础(实际上很可能),那么您可能会想将“重复的”代码移入该基础。 Do not do that; 不要那样做; it will cause the solution to break. 它将导致解决方案破裂。

The tricky part is that inside MultipleChoice , when you do view.ProcessCriteria(this); 棘手的部分是在MultipleChoice ,当您执行view.ProcessCriteria(this); the compiler can infer that the static type of this is IChoices -- this is where the redirection happens! 编译器可以推断出静态类型的thisIChoices -这是重定向发生之地! If you move the call to ProcessCriteria inside a hypothetical base class CriteriaBase : ICriteria , then the type of this will become ICriteria and the dispatch of the call to the appropriate ICriteriaView.ProcessCriteria overload will no longer work. 如果您将呼叫ProcessCriteria一个假想的基本类中CriteriaBase : ICriteria ,然后类型this将成为ICriteria并调用相应的调度ICriteriaView.ProcessCriteria过载将不再工作。

You could do this: 您可以这样做:

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

But polymorphism is probably your best bet. 但是,多态可能是您最好的选择。

That is not the best way. 那不是最好的方法。 If you are performing different actions based on the type of an object, you should probably be using polymorphism instead for a myriad number of reasons. 如果要根据对象的类型执行不同的操作,则出于多种原因,您可能应该使用多态。

How you use polymorphism depends on what you actually need to have done based on the different types of ICriteria that are being used. 使用多态的方式取决于所使用的ICriteria的不同类型,实际需要完成的工作。 If you just need to get a string containing all of their members, you could easily add a method to ICriteria and hand the responsibility to the class itself instead of the code that depends on it. 如果只需要获取包含其所有成员的字符串,则可以轻松地向ICriteria添加方法,并将责任移交给类本身,而不是依赖于该类的代码。 This reduces duplication, puts code in a logical place, and makes sure you don't forget to add code for a new type of ICriteria. 这样可以减少重复,将代码放在合理的位置,并确保您不会忘记为新型ICriteria添加代码。

If you give us more information on how you want different types to be treated/behave, we can probably give you more specific advice. 如果您向我们提供了有关如何处理/操作不同类型的更多信息,我们可能会为您提供更具体的建议。 :D :D

Here is a long term solution to an ever expanding list of critera without having to add more if/then/else. 这是不断增长的critera列表的长期解决方案,而无需添加更多if / then / else。

While this code is complex to someone not used to designing in this manner, it allows you to keep your method dealing with criteria the same, and just register new delegates to handle additional criteria. 尽管对于不习惯以这种方式进行设计的人来说,这段代码很复杂,但是它使您可以使处理准则的方法保持相同,并且只需注册新的委托即可处理其他准则。

The idea is to create a map of Type objects that hold delegates in which to execute. 这个想法是创建一个Type对象的映射,其中包含要在其中执行的委托。 You can then register new delegates to execute based on new Type s as you generate them. 然后,您可以在生成新委托时注册新委托,以根据新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");
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM