是否可以在没有强制转换的情况下在C#中实现“虚拟构造函数”模式?

时间:2021-03-13 21:06:03

I'm writing a program that writes C# that eventually gets compiled into an application. I would like each of the generated types to provide a "deep clone" function which copies the entire tree of data. That is, I want someone to be able to do:

我正在编写一个编写C#的程序,最终编译成一个应用程序。我希望每个生成的类型都提供一个“深度克隆”功能,它复制整个数据树。也就是说,我希望有人能够做到:

var x = new Base(); // Base has public virtual Base DeepClone() { ... }
var y = new Derived(); // Derived overrides DeepClone
Base a = x.DeepClone();
Base b = y.DeepClone();
// Derived c = x.DeepClone(); // Should not compile
Derived d = y.DeepClone(); // Does not compile, DeepClone returns Base

instead of

var x = new Base();
var y = new Derived();
Base a = x.DeepClone();
Base b = y.DeepClone();
// Derived c = x.DeepClone(); // Should not compile
Derived d = (Derived)y.DeepClone();

However, C# doesn't allow you to do this in a simple override; the overrides must return the same type as the declared type on the base.

但是,C#不允许您在简单的覆盖中执行此操作;覆盖必须返回与基础上声明的类型相同的类型。

Since I'm writing code that stamps out boilerplate anyway, is there something I can generate to allow the first block to compile? I tried something similar to the following:

既然我正在编写用于标记样板文件的代码,那么我可以生成一些允许第一个块编译的东西吗?我尝试了类似以下的东西:

abstract class Base
{
    public abstract Base DeepClone();
}

class Base2 : Base
{
    int Member { get; set; }

    public Base2() { /* empty on purpose */ }
    public Base2(Base2 other)
    {
        this.Member = other.Member;
    }

    public override Base2 DeepClone()
    {
        return new Base2(this);
    }
}

sealed class Derived : Base2
{
    string Member2 { get; set; }

    public Derived() { /* empty on purpose */ }
    public Derived(Derived other)
        : base(other)
    {
        this.Member2 = other.Member2;
    }

    public override Derived DeepClone()
    {
        return new Derived(this);
    }
}

but this does not compile because the overrides don't match. I also tried overriding the method from the base and hiding it with the "new" keyword, but this didn't work either.

但这不会编译,因为覆盖不匹配。我也尝试从基础覆盖该方法并使用“new”关键字隐藏它,但这也不起作用。

2 个解决方案

#1


Yes it is doable, but you must move your abstract method from being public to being protected then make a public non abstract function that just calls the protected method. The derived classes just need to implement the protected function and can shadow the public function, performing the cast that would have been performed by the client.

是的,它是可行的,但您必须将您的抽象方法从公共移动到受保护,然后创建一个只调用受保护方法的公共非抽象函数。派生类只需要实现受保护的函数,并且可以隐藏公共函数,执行客户端执行的转换。

abstract class Base
{
    public Base DeepClone()
    {
        return CloneInternal();
    }

    protected abstract Base CloneInternal();
}

class Base2 : Base
{
    int Member { get; set; }

    public Base2() { /* empty on purpose */ }
    public Base2(Base2 other)
    {
        this.Member = other.Member;
    }

    new public Base2 DeepClone()
    {
        return (Base2)CloneInternal();
    }

    protected override Base CloneInternal()
    {
        return new Base2(this);
    }
}

sealed class Derived : Base2
{
    string Member2 { get; set; }

    public Derived() { /* empty on purpose */ }
    public Derived(Derived other)
        : base(other)
    {
        this.Member2 = other.Member2;
    }

    new public Derived DeepClone()
    {
        return (Derived)CloneInternal();
    }

    protected override Base CloneInternal()
    {
        return new Derived(this);
    }
}

#2


Here is a way to do this that doesn't involve any casting. It doesn't allow you to do new Base() from your first snippet, which doesn't make any sense because it is abstract, but the rest works:

这是一种不涉及任何铸造的方法。它不允许你从你的第一个片段做新的Base(),这没有任何意义,因为它是抽象的,但其余的工作:

interface Base
{
    Base DeepClone();
}

abstract class Base<T>: Base where T: Base<T>
{ 
    public abstract T DeepClone();
    Base Base.DeepClone() {
        return DeepClone();
    }
}

class Base2 : Base<Base2> 
{ 
    public override Base2 DeepClone() 
    {
        return new Base2();
    } 
}

Then, in your Main method:

然后,在您的Main方法中:

public static void Main()
{
    var y = new Base2(); // Base2 overrides DeepClone
    Base b = y.DeepClone();
    Base2 c = y.DeepClone(); // Compiles an works
}

#1


Yes it is doable, but you must move your abstract method from being public to being protected then make a public non abstract function that just calls the protected method. The derived classes just need to implement the protected function and can shadow the public function, performing the cast that would have been performed by the client.

是的,它是可行的,但您必须将您的抽象方法从公共移动到受保护,然后创建一个只调用受保护方法的公共非抽象函数。派生类只需要实现受保护的函数,并且可以隐藏公共函数,执行客户端执行的转换。

abstract class Base
{
    public Base DeepClone()
    {
        return CloneInternal();
    }

    protected abstract Base CloneInternal();
}

class Base2 : Base
{
    int Member { get; set; }

    public Base2() { /* empty on purpose */ }
    public Base2(Base2 other)
    {
        this.Member = other.Member;
    }

    new public Base2 DeepClone()
    {
        return (Base2)CloneInternal();
    }

    protected override Base CloneInternal()
    {
        return new Base2(this);
    }
}

sealed class Derived : Base2
{
    string Member2 { get; set; }

    public Derived() { /* empty on purpose */ }
    public Derived(Derived other)
        : base(other)
    {
        this.Member2 = other.Member2;
    }

    new public Derived DeepClone()
    {
        return (Derived)CloneInternal();
    }

    protected override Base CloneInternal()
    {
        return new Derived(this);
    }
}

#2


Here is a way to do this that doesn't involve any casting. It doesn't allow you to do new Base() from your first snippet, which doesn't make any sense because it is abstract, but the rest works:

这是一种不涉及任何铸造的方法。它不允许你从你的第一个片段做新的Base(),这没有任何意义,因为它是抽象的,但其余的工作:

interface Base
{
    Base DeepClone();
}

abstract class Base<T>: Base where T: Base<T>
{ 
    public abstract T DeepClone();
    Base Base.DeepClone() {
        return DeepClone();
    }
}

class Base2 : Base<Base2> 
{ 
    public override Base2 DeepClone() 
    {
        return new Base2();
    } 
}

Then, in your Main method:

然后,在您的Main方法中:

public static void Main()
{
    var y = new Base2(); // Base2 overrides DeepClone
    Base b = y.DeepClone();
    Base2 c = y.DeepClone(); // Compiles an works
}