简体   繁体   English

C#中抽象基类中的派生类实例化

[英]Derived class instantiation in abstract base class in C#

My aim is to write an abstract base class which contains a method for deriving “child instances”. 我的目标是编写一个抽象基类,其中包含一个派生“子实例”的方法。 In this method already some computation is done which is common in all deriving classes. 在该方法中,已经进行了一些计算,这在所有派生类中是常见的。

The difficulty is that the base class is not able to create the child class on its own. 困难在于基类无法自己创建子类。 So I introduced a type parameter T in my base class and a protected abstract method which shall return an instance of T . 所以我在我的基类中引入了一个类型参数T和一个protected abstract方法,该方法将返回一个T的实例。

public abstract class Base<T> where T : Base<T>
{
    public T GetChild()
    {
        string param = ComplexComputation();
        return NewInstanceFrom(param);
    }

    protected abstract T NewInstanceFrom(string param);
}

// --- somewhere else: ---

public class Derivative : Base<Derivative>
{
    public Derivative() { }

    protected sealed override Derivative NewInstanceFrom(string param)
    {
        return new Derivative(param);
    }

    private Derivative(string param)
    {
        // some configuration
    }
}

The disadvantage of this approach is that I cannot ensure that NewInstanceFrom is only invoked by the base class. 这种方法的缺点是我不能确保只有基类调用NewInstanceFrom It could also be invoked by classes inheriting from Derivative . 它也可以由继承Derivative的类调用。 That's what I want to avoid. 这就是我想要避免的。

So I could encapsulate the functionality in a private class or delegate: 所以我可以将功能封装在私有类或委托中:

public abstract class Base<T> where T : Base<T>
{
    public T GetChild()
    {
        string param = ComplexComputation();
        return subElementDerivator(param);
    }

    protected Base<T>(Func<string, T> subElementDerivator)
    {
        this.subElementDerivator = subElementDerivator;
    }

    private Func<string, T> subElementDerivator;
}

// --- somewhere else: ---

public class Derivative : Base<Derivative>
{
    public Derivative()
        : base(deriveSubElement)    
    {
    }

    private Derivative(string param)
        : base(deriveSubElement)
    {
        // some configuration
    }

    private static Derivative deriveSubElement(string param)
    {
        return new Derivative(param);
    }
}

But this introduces a new object. 但这引入了一个新的对象。

Is there a simpler way to prevent access to a functionality (which the base class shall have access to) from heirs of Derivative ? 是否有更简单的方法来阻止Derivative继承人访问功能(基类可以访问)?

You can use explicit interface implementation to hide your factory method. 您可以使用显式接口实现来隐藏工厂方法。 Any client can still call the Create method after casting but at least intellisense won't help developers. 任何客户端仍然可以在转换后调用Create方法,但至少intellisense不会帮助开发人员。

public interface ISecretFactory<T>
{
    T Create(string param);
}

public abstract class Base<T> where T : Base<T>, ISecretFactory<T>
{
    public T GetChild()
    {
        // We are sure type T always implements ISecretFactory<T>
        var factory = this as ISecretFactory<T>;
        return factory.Create("base param");
    }
}

public class Derivative : Base<Derivative>, ISecretFactory<Derivative>
{
    public Derivative()
    {

    }

    private Derivative(string param)
    {

    }

    Derivative ISecretFactory<Derivative>.Create(string param)
    {
        return new Derivative(param);
    }
}

public class SecondDerivative : Derivative
{
    public void F()
    {
        // intellisense won't show Create method here.
        // But 'this as ISecretFactory<Derivative>' trick still works.
    }
}

The additional object can be avoided by moving the ComplexComputation to the constructor of the base class and making the GetChild method abstract to let the deriving class pick a correct constructor there. 通过将ComplexComputation移动到基类的构造函数并使GetChild方法抽象为允许派生类在那里选择正确的构造函数,可以避免附加对象。

But how to return the computed value param in base constructor to the invoking derivate constructor? 但是如何将基础构造函数中的计算值param返回给调用派生构造函数? A possibility is to use the out parameter modifier. 可能是使用out参数修饰符。 But because in C# 5.0 we are unfortunately not able to declare the variables before (or within) the base constructor call, we need to take the parameter along in the derivative constructor. 但是因为在C#5.0中,遗憾的是我们无法在基础构造函数调用之前(或之内)声明变量,我们需要在派生构造函数中获取参数。

public abstract class Base<T> where T : Base<T>
{
    public abstract T GetChild();

    protected Base(T parent, out string param)
    {
        param = ComplexComputation();
    }

    protected Base()
    {
    }
}

// --- somewhere else: ---

public class Derivative : Base<Derivative>
{
    public sealed override Derivative GetChild()
    {
        string param;
        return new Derivative(this, out param);
    }

    public Derivative() { }

    private Derivative(Derivative parent, out string param)
        : base(parent, out param)
    {
        // some configuration
    }
}

In my case I could leave the param from the constructors away, instead I stored it in a public property. 在我的情况下,我可以将构造函数中的param留下,而是将其存储在public属性中。

This approach except the pesky necessary hack looks relatively clean to me, but it does not “scale” when multiple overloadings of GetChild are necessary. 除了讨厌的必要hack之外,这种方法对我来说看起来相对干净,但是当需要多次重载GetChild时它不会“缩放”。

Maybe in C# 6.0 it is possible to declare the param directly in the base constructor invocation. 也许在C#6.0中,可以直接在基础构造函数调用中声明param https://msdn.microsoft.com/de-de/magazine/dn683793.aspx https://msdn.microsoft.com/de-de/magazine/dn683793.aspx

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

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