Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#-从基类型选择多态方法_C#_Reflection_Polymorphism - Fatal编程技术网

C#-从基类型选择多态方法

C#-从基类型选择多态方法,c#,reflection,polymorphism,C#,Reflection,Polymorphism,假设有一种称为GetInstance()的工厂方法,它构建一个类并返回一个基实例 抽象类基类{} 类类型1:基类{} 类类型2:基类{} 基类GetInstance() { //返回Type1或Type2实例 } 我想执行两个方法中的一个,将具体类实例作为参数,而不是基类 void DoSomething(类型1实例) { //对类型1实例执行一些操作 } void DoSomething(类型2实例) { //对类型2实例执行一些操作 } 这样做的丑陋方式,显然打破了开闭原则,就是迭代所有

假设有一种称为
GetInstance()
的工厂方法,它构建一个类并返回一个基实例

抽象类基类{}
类类型1:基类{}
类类型2:基类{}
基类GetInstance()
{
//返回Type1或Type2实例
}
我想执行两个方法中的一个,将具体类实例作为参数,而不是基类

void DoSomething(类型1实例)
{
//对类型1实例执行一些操作
}
void DoSomething(类型2实例)
{
//对类型2实例执行一些操作
}
这样做的丑陋方式,显然打破了开闭原则,就是迭代所有可能的类型,调用适当的方法

void GetAndInvokeBad()
{
基类实例=GetInstance();
//选择正确多态方法的糟糕方法
交换机(实例)
{
案例类型1实例:
剂量测定法(实例);
打破
案例类型2实例:
剂量测定法(t2例);
打破
}
}
一种更通用的方法是使用反射来查找可以采用类实例物理类型的所有方法

void GetAndInvokeGood()
{
基类实例=GetInstance();
var method=GetType().GetMethods()
其中(m=>m.Name==“DoSomething”)
.First(m=>m.GetParameters()[0]。ParameterType==instance.GetType());
调用(这个新对象[]{instance});
}

我只是想知道,这是一个好的模式吗?有没有一种比使用反射更好、更受认可的方式来切换子类型?

一种解决方案是使用策略模式。(不要把这个例子当作字面上的例子,因为我是从头开始写的,如果不在IDE中检查它,我可能会有一些语法错误,但它给出了要点。)

公共类InstanceHandler{
//依赖项在运行时注入或加载。
私有IEnumerable_declaredTypes;
公共无效剂量测定(对象obj){
var result=_delcaredTypes.SingleOrDefault(x=>x.IsType(obj));
如果(结果==null){
抛出新的NotSupportedException($“不支持类型{obj}”);
}
结果:剂量测定法(obj);
}
}
公共抽象类BaseInstance(){
公共抽象布尔IsType(对象类型);
公共抽象空值(对象类型);
}
公共类A:BaseInstance{
公共覆盖布尔IsType(对象类型){
返回true;//检查类型是否与实例匹配的逻辑。
}
公共覆盖无效DoSomething(对象类型){
var castType=(ExpectedType)类型;
//做点什么。
}
}

您还可能变得非常脏,并使用
动态
。不是每个人都喜欢它;我避免使用它,除非它对特定场景太有用,然后对它进行大量的评论。在这里,它提供了一个非常干净的实现

使用
dynamic
可以防止代码分析器检查代码并发现问题,因此在考虑使用它时请注意这一点

最后,这里有一个关于使用
dynamic
作为参考的例子

using System;

namespace SomeNamespace
{
    public class Program
    {
        static void Main()
        {
            dynamic instance1 = GetInstance(true); //gets Type1
            dynamic instance2 = GetInstance(false); //gets Type2

            DoSomething(instance1); //prints "Type1 did something"
            DoSomething(instance2); //prints "Type2 did something"
        }

        static BaseClass GetInstance(bool type1)
        {
            // returns either Type1 or Type2 instance
            return type1 ? (BaseClass)(new Type1()) : (BaseClass)(new Type2());
        }

        static void DoSomething(Type1 instance)
        {
            Console.WriteLine("Type1 did something");
        }

        static void DoSomething(Type2 instance)
        {
            Console.WriteLine("Type2 did something");
        }
    }

    abstract class BaseClass { }

    class Type1 : BaseClass { }

    class Type2 : BaseClass { }
}
如果您不想将变量作为
dynamic
传递,也可以在最后一英里处强制转换为
dynamic
,例如:

DoSomething((dynamic)instance);

为什么不重载
GetInstance
返回特定类型?寻找“C#中的双重分派”策略模式可以让您在不违反打开/关闭原则的情况下在案例之间切换。@JohnathanBarclay C#不支持返回类型协方差-@Steztric重载不覆盖。然而,这是一个糟糕的措辞选择;我只是想使用不同的方法返回特定的类型,重载或其他。我明白了!太好了。有可能制造出这种反变体吗?例如,定义
BaseInstance,其中T是基类
,那么抽象方法可以是
公共抽象void DoSomething(T type)
,然后A可以是
类A:BaseInstance
,方法可以是
公共覆盖void DoSomething(Type1 type)
。这将克服在方法中强制转换的需要。@Steztric如果您有机会尝试一下,这是可能的。但是,在我看来,在同一个集合中存储具有不同泛型类型的声明类型会有问题。也许您可以围绕类型创建一个包装器,将它们存储在同一个集合中。这一点很好。如果我这样做了,那么我就不能将这些处理程序存储在
\u declaredTypes
DoSomething((dynamic)instance);