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

问题描述

想象一下,有一种叫做GetInstance()的工厂方法可以建立一个类并返回一个基本实例;

abstract class BaseClass { }

class Type1 : BaseClass { }

class Type2 : BaseClass { }

BaseClass GetInstance()
{
    // returns either Type1 or Type2 instance
}

我想执行将具体类实例作为参数而不是基类的两个方法之一

void DoSomething(Type1 instance)
{
    // do something with type 1 instance
}

void DoSomething(Type2 instance)
{
    // do something with type 2 instance
}

这样做的丑陋方法明显地打破了开放-封闭原则,只是迭代所有可能的类型,并调用适当的方法

void GetAndInvokeBad()
{
    BaseClass instance = GetInstance();
    
    // Sucky way of selecting the right polymorphic method
    switch (instance)
    {
        case Type1 t1instance:
            DoSomething(t1instance);
            break;
        case Type2 t2instance:
            DoSomething(t2instance);
            break;
    }
}

一种更通用的方法是使用反射来查找所有可以采用类实例物理类型的方法

void GetAndInvokeGood()
{
    BaseClass instance = GetInstance();

    var method = GetType().getmethods()
        .Where(m => m.Name == "DoSomething")
        .First(m => m.GetParameters()[0].ParameterType == instance.GetType());

    method.Invoke(this,new object[] {instance});
}

我只是想知道,这是一个好模式吗?是否有比使用反射更好的,更公认的方式来打开子类型?

解决方法

一种解决方案是使用策略模式。 (不要把这个例子当做我从头开始写的文字例子,如果不在IDE中检查它,我可能会有一些语法错误,但这虽然很重要。)

public class InstanceHandler {
    //Dependency inject or load at runtime.
    private IEnumerable<BaseClass> _declaredTypes;

    public void DoSomething(object obj) {
        var result = _delcaredTypes.SingleOrDefault(x => x.IsType(obj));
        if(result == null) {
            throw new NotSupportedException($"The type '{obj}' is not supported.");
        }
        result.DoSomething(obj);
    }
}

public abstract class BaseInstance() {
    public abstract bool IsType(object type);
    public abstract void DoSomething(object type);
}

public class A : BaseInstance {
    public override bool IsType(object type) {
        return true; //Logic to check if type is matching instance.
    }

    public override void DoSomething(object type) {
        var castType = (ExpectedType) type;
        //Do Something.
    }
}
,

您也可能会变得很脏,并使用dynamic。不是每个人都喜欢它。我会避免使用它,除非它在某些情况下不太有用,然后再对其进行大量评论。在这里,它实现了非常干净的实现。

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

最后,这是SO discussion,有关使用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);