创建一个抽象函数,它根据继承者采用不同的参数

Creating an abstract function which takes different arguments depending on inheritor

我偶然发现了一个问题,我想不出一个好的解决方法。

我的程序中有一些对象需要在发生某些事件时重新初始化。我需要重新初始化的对象是抽象 class.

的子 classes

虽然这似乎是一个糟糕的方法,但我决定在抽象 class 中有一个抽象的重新初始化函数,并让所有子classes 实现该函数。但是现在有一个问题,每个子 class 在它们的构造函数中采用不同的参数。

我的最小示例如下所示:

abstract class Base
{
    public abstract void Init(/* Some parameter */);
}

class SubA : Base
{
    public SubA(int a)
    {
        Init();
    }

    public override void Init(int a)
    {
        //do stuff with int
    }
}

class SubB : Base
{
    public SubB(string b)
    {
        Init();
    }

    public override void Init(string b)
    {
        //do stuff with string
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Base> stuff = new List<Base>()
        {
            new SubA(65),
            new SubB("B")
        };

        foreach (var s in stuff)
        {
            s.Init();
        }
    }
}`

我确实相信这个解决方案有代码味道,所以欢迎任何避免这种情况的建议。如果需要任何其他信息来帮助我,请告诉我。

我认为您可能需要改用接口。

public interface IBase
{
    void Init();
}

public class SubA : IBase
{
    private int _a;
    public SubA(int a)
    {
        _a = a;
    }
    public void Init()
    {
        // do stuff with _a
    }
}

public class SubB : IBase
{
    private string _s;
    public SubB(string s)
    {
        _s = s;
    }
    public void Init()
    {
        // do stuff with _s
    }
}

public class SubC : IBase
{
    private int _a;
    private string _s;
    public SubC(int a, string s)
    {
        _a = a;
        _s = s;
    }

    public void Init()
    {
        // do stuff with _a and _s
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<IBase> stuff = new List<IBase>()
        {
            new SubA(65),
            new SubB("B"),
            new SubC(100, "Z"),
        };

        foreach (IBase sub in stuff)
        {
            sub.Init();
        }
    }
}

好吧,解决方案很简单,只需在私有字段中声明 specific 变量,然后在 paramaterless Init 方法中使用这些字段,像这样:

class SubA : Base
{
    private int _a;
    public SubA(int a)
    {
        _a = a;
        Init();
    }

    public override void Init()
    {
        //do stuff with int _a
    }
}

class SubB : Base
{
    private string _b;
    public SubB(string b)
    {
        _b = b;
        Init();
    }

    public override void Init()
    {
        //do stuff with string _b
    }
}

此外,您可以改为抽象 class 接口,因此实现它的 classes 也将能够从其他基础 classes 继承。

令人困惑的是,您为什么要以一种循环方式重新初始化项目集合,使每个项目都获得一个新值。您的代码有很多问题,但我会尝试解释它们,因为您的示例无法编译。

首先,如果你想在子class SubA中调用Init,你必须像这样在SubA中实现它。

abstract class Base
{
    public abstract void Init()
}

class SubA : Base
{
    public SubA(int a)
    {
        this.Init();
    }

    public override void Init()
    {
        // do stuff
    }

    public void Init(int a)
    {
        //do stuff with int
    }
}

如果你想调用init而不覆盖它,那么抽象基class需要这样实现

abstract class Base
{
    public void Init()
    {
        //do stuff in the implementation by the abstract class
    }
}

class SubA : Base
{
    public SubA(int a)
    {
        this.Init();
    }

    public void Init(int a)
    {
        //do stuff with int
    }
}

看来您想要的是能够重新初始化 Base 的任何子 class,并可选择让子 class 执行自定义初始化.

如果你想让它们重新初始化到它们的原始状态,你可以将它们的原始状态存储在实例中,并使用被覆盖的 Init() 来恢复。

class Foo : Base
{
    private int _iOrig;
    private string _sOrig;

    public int i { get; set; }
    public string s { get; set; }

    public Foo(int i, string s)
    {
        this.i = i;
        this._iOrig = i;
        this.s = s;
        this._sOrig = s;
    }

    public override void Init()
    {
        this.i = _iOrig;
        this.s = _sOrig;
    }
}

如果一切都将是抽象的,您甚至可以考虑使用接口。

如果您想使用外部数据重新初始化它们,您可以遍历它们并检查它们是什么类型,然后调用它们自己的 Init 方法,这意味着 Base 不会需要有一个abstract Init(),而子classes可以实现他们想要的任何东西。 (虽然这似乎不是你想要的)。

这可以使用转换检查在循环中完成。

private void InitAll(IEnumerable<Base> collection)
{   

    foreach(Base b in collection)
    {
        var sa = b as SubA;
        if(sa != null)
        {
            sa.Init(getInt());
            continue;
        }

        var sb = b as SubB;
        if (sb != null)
        {
            sb.Init(getString());
            continue;
        }
    }
}