繁体   English   中英

为什么我不能在抽象 C# class 上创建抽象构造函数?

[英]Why can't I create an abstract constructor on an abstract C# class?

我正在创建一个抽象 class。我希望我的每个派生类都被迫实现构造函数的特定签名。 因此,如果我想强迫他们实施一种方法,我做了我会做的事情,我做了一个抽象的方法。

public abstract class A
{
    abstract A(int a, int b);
}

但是我收到一条消息,说抽象修饰符对此项目无效。 我的目标是强制执行这样的代码。

public class B : A
{
    public B(int a, int b) : base(a, b)
    {
        //Some other awesome code.
    }
}

这是所有 C# .NET 代码。 谁能帮我吗?

更新 1

我想补充一些东西。 我最终得到的是这个。

private A() { }

protected A(int a, int b)
{
    //Code
}

这就是一些人所说的,默认是私有的,class 需要实现一个构造函数。 但是,这不会强制使用签名为 A(int a, int b) 的构造函数。

public abstract class A
{
    protected abstract A(int a, int b)
    {


    }
}

更新 2

我应该很清楚,为了解决这个问题,我将我的默认构造函数设为私有,而我的其他构造函数被保护。 我并不是真的在寻找一种方法来使我的代码正常工作。 我照顾好了。 我想了解为什么 C# 不允许您这样做。

您不能拥有抽象构造函数,因为抽象意味着您必须在任何非抽象子类中覆盖它,并且您不能覆盖构造函数。

如果你考虑一下,这是有道理的,因为你总是调用子类的构造函数(使用new运算符)而不是基类。

一般来说,C#中强制执行特定构造函数签名的唯一方法是使用new()泛型约束,该约束强制存在type参数的无参数构造函数。

将A类中的构造函数更改为

protected A(int a, int b)
{
    // Some initialisation code here
}

然后你的子类必须使用它,因为没有默认的构造函数。

但是,它们仍然可以更改构造函数的实际签名。 据我所知,没有办法强制子类为其构造函数使用特定的签名。 我很确定构造函数不能是抽象的。

你到底需要什么? 我们或许可以建议解决这个问题。

虽然您不能覆盖构造函数,因此无法定义抽象构造函数,但您可以在抽象基类中放置抽象工厂方法。 所有派生类都需要覆盖它。

public abstract class A 
{ 
    abstract A MakeAInstance(int a, int b); 
} 

public class B : A 
{ 
    // Must implement:
    override A MakeAInstance(int a, int b) {
        // Awesome way to create a B instance goes here
    }
} 

多种原因:

1)构造函数不是继承的,因此您无法覆盖它们。

2)构造函数是一个静态成员函数,因为它不需要调用特定的实例。 Abstract暗示“虚拟”,这意味着实现可以根据特定实例的子类化方式而有所不同,这与“static”关键字含义的意图相反。

您不能强制执行构造函数签名,因为每个派生类可能(必须!)定义自己的构造函数,并且它们可以采用他们喜欢的任何参数。

如果需要将给定的变量集传递给派生类的对象,请定义一个需要由派生类实现的抽象方法。 如果类没有实现抽象方法,则会出现编译器错误。

希望这会帮助某人newb,因为我正在寻找一个“正常”的公共课,其中一个ctor接受争论然后我需要让它成为一个儿童类,所以我需要一个空的ctor。

我以前不知道这个:如果你创建一个受保护的ctor然后子类看到它但程序的其余部分看不到它(我想我的困惑是因为在asp.net我看到aspx页面中的所有受保护继承自cs ...)

不确定这是否有帮助 - 但我觉得这将是你的问题的解决方案:

public class A
{
  public A(int a, int b)
  {
    DoSomething(int a, int b);
  }

  virtual public void DoSomething(int a, int b)
  {

  }
}

public class B : A
{
  override public void DoSomething(int a, int b)
  {
    //class specific stuff
  }
}

结果是,您可以使用正确的行为在任何派生类中调用具有所需参数的构造函数。

所有子类总是可以指定自己的构造函数,只要它们调用超类的构造函数 - 所以没有办法强制类具有特定的构造函数(至少,这是它在Java中的工作方式)。 您可以看到使用工厂模式将您带到某处 - 您可以在接口中定义工厂方法 - 但是您需要一个单独的工厂类,因为您的抽象基类不知道需要的对象的实际类创建。

但是:可能会添加一个更具体的问题示例,可能会提示其他/更好的响应。 您是否在寻找一些通用的实例化代码,或者您是否担心必须在抽象基类上进行特定设置?

如果您只关心必须由抽象类完成的初始化,请创建一个方法来执行此操作,并记录该方法的用法。

C# 11 和 .NET 7 在接口中引入了 static 虚拟成员,它们可用于(尽管是以人为的方式)强制执行具有给定签名的工厂方法。

public interface IBase<TDerived> where TDerived : Base<TDerived>, IBase<TDerived>
{
    public static abstract TDerived CreateInstance(int a, int b);
}

public abstract class Base
{
    private protected Base(int a, int b) { }
}

public abstract class Base<TDerived> : Base where TDerived : Base<TDerived>, IBase<TDerived>
{
    protected Base(int a, int b) : base(a, b) { }
}

然后,在其他一些组件中

public class Derived : Base<Derived>, IBase<Derived>
{
    static Derived IBase<Derived>.CreateInstance(int a, int b)
    {
        return new(a, b);
    }

    public Derived(int a, int b) : base(a, b) { }
}

诚然,这个解决方案有点代码味,它有一个private protected基 class 构造函数、一个中间基 class 和一个必须实现的单独interface 不幸的是,在class es 中没有static abstract / static virtual methods(还没有?),但尽管有异味,这个解决方案仍然有效。

我正在使用此模式从非托管数据创建托管 class 实例。 我有一个可扩展的Base class 并且可以随时添加新的Derived类(甚至来自其他程序集),因此我无法确定性地 select 正确的 class 在编译时实例化,但我可以使用运行时数据来确定这一点。 我的Base class 拥有一个static Dictionary<int, Func<int, int, Base>> 然后在Base<TDerived> class 构造函数中,我填充该字典(在Derived类的第一次实例化时将新键添加到字典中)。

protected Base(int a, int b) : base(a, b) // Base<TDerived>
{
    _ = instanceCreators.TryAdd(GetKey(a, b), TDerived.CreateInstance);
    // Func is covariant and accepts the more derived return type
    // GetKey maps the unmanaged data to a unique key
}

从那里,我的Base class 可以返回任何Derived类的完整实例:

public static Base GetDerived(int a, int b) // in Base class
{
    return instanceCreators[GetKey(a, b)](a, b); // calls Derived class constructor
}

因为可以添加新的Derived类型,甚至可以从程序集外部添加,所以没有更优雅的解决方案来从Base class 创建我需要的实例。希望static abstract方法也将扩展为abstract class es,以便interface可以被删除,至少。 这种方法暂时有效。

编辑:不知何故,我忽略了private protected访问修饰符的引入,它比使用internal更适合这种荒谬的方法。

暂无
暂无

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

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