在 Inherited Class Constructor C# 中强制执行部分基础初始化(和方法)(就像抽象对方法所做的那样) - 解决这个问题

Forcing partial base Initialization (and methods) in Inherited Class Constructor C# (like abstract does to methods) - work around to do it

我有一个 C# abstract class 有一些方法可以通过其 children 实现。

虽然如此,但children的初始化值由两部分组成,一部分与parent相同,另一部分为[=37]独有=]仁.

public abstract class parentClass {
    public abstract bool IsInputValid(string input); //children must implement this
    public parentClass () {
       //Some shared initialization
    }
}

如果 class 不是 abstract 我们可以做这样的事情来实现

public class parentClass {
    public parentClass (string input) {
       //Some shared initialization
    }
}

public class childClass : parentClass {
    public childClass (string input) : base (input) {
       //Some unique initialization
    }
}

但这不能使用 abstract class 和其他方法来完成,该方法不需要实现(因为它不是抽象的)。

所以我在这里进退两难。一方面,我想调用一些基础初始化,另一方面,我也想强制执行一些方法。

所以我的问题是,我们通常如何实施这种情况?一方面它强制执行一些基础初始化,另一方面强制执行一些方法。

注意:我是 abstract class 的新手,所以我很乐意收到有关它的任何意见。

我在哪里申报错误(如果有)?如果我们不能这样做,是否有办法绕过产生相同的结果(即强制 child class 对构造函数使用特定签名)?

您不能强制执行派生的 classes 的构造函数的签名甚至存在。(或任何 class 就此而言)

恐怕故事就这样结束了。你没有做错任何事,只是不可能。

你不能有抽象构造函数,但也没有必要。 您需要做的就是从 parentClass 中删除 "abstract" 关键字,然后就可以开始了。

Since you can't override constructors in c#, 您不能在派生的 class 中强制存在某个构造函数。

这意味着:

  • 构造函数不能是抽象的、虚拟的等
  • 构造函数不是多态的

应该没有必要强制执行。你说基础 class 有一些共同的初始化,child classes 也有自己的专门初始化。

这已经强制执行,如果你有:

public abstract class Base
{
    protected Base(int value) { ... }
}

那么你有两个保证:

  • 没有人可以构造 Base 类型的 object,因为它是抽象的
  • 没有人可以构造一个继承自 Base 的 object,而无需间接调用 Base 的唯一现有构造函数,该构造函数采用 int value 参数。

最后一部分很重要。

一个childclass至少可以通过三种方式处理这种类型的基类构造函数:

  • 它可以提供一个看起来相同的构造函数,保存它的名称,只是将值传递给基本构造函数:

    public class Child : Base
    {
        public Child(int value) : base(value) { ... }
    }
    
  • 它可以提供一个构造函数,该构造函数具有此参数,但还具有 child class 构造函数的附加参数:

    public class Child : Base
    {
        public Child(int value, string other) : base(value) { ... }
    }
    
  • 它可以提供一个构造函数,它没有参数给基class,但设法计算这个参数:

    public class Child : Base
    {
        public Child(string other) : base(other.Length) { ... }
    }
    

最后一部分还处理了 child 构造函数根本没有参数的情况:

public class Child : Base
{
    public Child() : base(new Random().Next(100)) { ... }
}

无论您使用哪种方法,都不可能在不为该参数传递值的情况下调用基础 class 构造函数,因此您必须执行以下操作:

  • Child classes 必须知道 基础class 构造函数及其参数

但是您不能也不应该尝试强制使用具有特定签名的特定构造函数。


现在,话虽如此,如果您想创建某种通用方法来构造两个具有如此不同构造函数的不同 child classes,该怎么办?使用它们的人不需要知道任何一个构造函数的细节?

输入factory pattern (Wikipedia):

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.

(从 Wikipedia-article 中的条目段落复制的引用文本)

这是一种抽象出这些不同构造函数和 child classes:

的存在和知识的方法
void Main()
{
    Test(new Child1Factory());
    Test(new Child2Factory());
}

public void Test(IBaseFactory baseFactory)
{
    Console.WriteLine("In Test(...");
    var b = baseFactory.Create();
}

public class Base
{
    public Base(int value)
    {
        Console.WriteLine($"Base.ctor({value})");
    }
}

public interface IBaseFactory
{
    Base Create();
}

public class Child1 : Base
{
    public Child1(int value) : base(value)
    {
        Console.WriteLine($"Child1.ctor({value})");
    }
}

public class Child1Factory : IBaseFactory
{
    public Base Create() => new Child1(42);
}

public class Child2 : Base
{
    public Child2(string name) : base(name.Length)
    {
        Console.WriteLine($"Child2.ctor({name})");
    }
}

public class Child2Factory : IBaseFactory
{
    public Base Create() => new Child2("Meaning of life");
}

要特别注意 Test(...) 方法,因为它不知道它会得到哪个 Base child,也不知道如何构造这样一个 object。如果您稍后从 Base 添加新的 child 类型,您还必须提供新的工厂,但不需要更改使用这些工厂的现有代码。

如果你想要一个更简单的工厂模式,你所要做的就是用委托替换接口和工厂classes:

void Main()
{
    Test(() => new Child1(42));
    Test(() => new Child2("Meaning of life"));
}

public void Test(Func<Base> baseFactory)
{
    Console.WriteLine("In Test(...");
    var b = baseFactory();
}

这里是最后的注释。由于工厂模式意味着您必须创建一个不同的类型来执行 object 的实际构造,您可以通过

强制执行其他类型的签名
  • 在工厂接口的Create方法中添加参数
  • 为工厂委托指定一个具有参数的委托

这意味着您可以强制执行 "the creation process" 的签名。尽管如此,您仍然不能强制特定构造函数的存在或签名,但构造函数只是达到目的的一种手段,创建 object,并且使用工厂模式,您实际上可以在代码中形式化此模式,因此您应该得到你想要的。