方法重载和继承——如何找到正确的方法?

Method overloading & inheritance - how to address to the right method?

我有以下工厂:

public class ChildFactory : IChildFactory {
    public Child? GetChild(ChildType childType)
    {
        switch (childType)
        {
            case ChildType.Boy:
                return new BoyChild() { };
            case ChildType.Girl:
                return new GirlChild() { };
            case ChildType.Undecided:
                return new UndecidedChild() { };
            default:
                return null;
         }
    }
}

每当我通过 ChildType 以生成 Child 时,我都会取回正确的子实例:

Child? child = _childFactory.GetChild((ChildType)Enum.Parse(typeof(ChildType), childDto.Type));

似乎在这一点上,child 是正确的类型(比方说 BoyChild)。 现在我想使用方法重载分别动态验证每个子类型。这是界面:

public interface IChildValidator
    {
        public ValidationResult Validate(BoyChild child);
        public ValidationResult Validate(GirlChild child);
        public ValidationResult Validate(UndecidedChild policy);
    }

但是每当我尝试在上面的方法中使用它时,我都会收到错误 CS1503: Cannot convert from 'Child' to 'BoyChild'

我想我应该以某种方式声明 child 不同,说“它可以是任何子类型”,但我不知道该怎么做。

提前致谢。

Overload Resolution 发生在编译时。 C# 编译器使用参数的静态类型来查找最佳重载。由于您使用 Child 类型的参数调用该方法,编译器找不到匹配的重载。三个重载中的 None 有一个类型为 Child 的参数。没有从 ChildBoyChildGirlChildUndecidedChild 的隐式转换。您可以将派生的 class 分配给基数 class,但反之则不行。

方法是使用多态,即向(抽象)Child class 添加抽象 Validate 方法(除非您可以提供标准实现在基地 class)。派生的 classes 必须覆盖此方法并提供实现。在运行时,Dynamic dispatch 将调用正确的实现。

要解决您的问题,您可以为 child 验证器使用抽象 class 而不是接口:

public abstract class ChildValidator
{
    public ValidationResult Validate(Child child)
    {
       switch(child)
       {
         case BoyChild boyChild:
            return Validate(boyChild);
         case GirlChild girlChild:
            return Validate(girlChild);
         case UndecidedChild undecided:
            return Validate(undecided);
         default:
            throw new Exception(child.GetType() + " is not supported");
       }
    }

    protected abstract ValidationResult Validate(BoyChild child);
    protected abstract ValidationResult Validate(GirlChild child);
    protected abstract ValidationResult Validate(UndecidedChild policy);
}

但我不推荐此解决方案。 如果您检查变量的类型,这通常(并非总是)表明您做错了什么。相反,我建议将验证逻辑放在 child class:

public abstract class Child
{
    public abstract ValidationResult Validate();
}

这有几个优点:如果您添加一个新的 child subclass,您不必记得在您的 ChildValidator 处也添加这个 class及其所有子classes。这使得扩展代码的行为变得更加容易,这也称为 open-closed 原则。此外,我猜您的验证确实会检查 child 的某些属性。如果添加 属性,您可能还必须更改验证方法。使用第二种方法,这些更改将集中在一个地方。

根据您正在寻求的分离级别,您可以尝试这样的事情来将验证逻辑保留在您的子模型之外(这只是一个例子,有几种方法可以调整这个,验证器的设置方式例如):

public class ChildFactory : IChildFactory
{
    public Child? GetChild(ChildType childType)
    {
        switch (childType)
        {
            case ChildType.Boy:
                return new BoyChild(new BoyValidator()) { };
            case ChildType.Girl:
                return new GirlChild(new GirlValidator()) { };
            case ChildType.Undecided:
                return new UndecidedChild(new UndecidedValidator()) { };
            default:
                return null;
        }
    }
}


public interface IChildValidator
{
    ValidationResult Validate(Child child);
}

public interface IChildValidatable
{
    ValidationResult Validate();
}


public class BoyValidator: IChildValidator
{
    ...
}


public class Child: IChildValidatable
{
    public Child(IChildValidator validator)
    {
        Validator = validator;
    }

    protected IChildValidator Validator { get; }


    public ValidationResult Validate() => Validator(this);
}

如果您喜欢那种“准备好覆盖”classes/函数的特定 classes 而不是基础 class,您可以改进前面的示例。它有时对验证覆盖很有用,因为您可能无论如何都会转换 Child。同样,这只是一个简单的例子,Validate(Child child) 函数可能会在传递给 TypedValidate 之前执行一些通用检查和额外的 class 检查。

public abstract class ChildValidator<T>: IChildValidator where T: Child
{
    public ValidationResult Validate(Child child) => TypedValidate(child as T)

    protected abstract ValidationResult TypedValidate(T child);
}

public class BoyValidator: ChildValidator<BoyValidator>
{
    protected override ValidationResult TypedValidate(BoyValidator child)
    {
        ...
    }
}