有没有办法在另一个抽象 class 中使用抽象泛型 class?

Is there a way to use an abstract generic class within another abstract class?

这个问题的措辞可能没有我想要的那么好,但我一直在努力做的是:

我有一个 运行 的应用程序并显示我从 TopCoder 解决的问题的结果。 由于所有这些问题都遵循相同的格式(接受一些输入,做某事,将输出与正确答案进行比较)我试图将通用功能收集到一个抽象的 class 问题中。

这是一个简化版:

public abstract class Problem
{
   // Some public fields and tasks exist here
   // Some protected methods for reporting progress exist here
   // Some abstract fields for things like the problem name and the link to the problem page exist here

#region Abstract Methods

protected abstract void RunExamplesOnce(CancellationToken token, IProgress<int> progress = null);

protected abstract void RunExamplesForAverage(CancellationToken token, IProgress<int> progress = null);

#endregion

#region Example Class

protected abstract class Example<T1, T2>
{
    public T1 Inputs;
    public T2 CorrectOutput;

    public Example(T1 inputs, T2 correctOutput)
    {
        this.Inputs = inputs;
        this.CorrectOutput = correctOutput;
    }

    public abstract (bool Pass, long ElapsedMilliseconds) TestExample();
}

#endregion

}

然后我为每个问题创建一个 class,例如TestProblem :问题并酌情覆盖。 我还创建了适当的 TestExample:示例,它将指定这些输入和输出的类型,并在 TestProblem 中创建这些输入和输出的数组。 到目前为止这一切都有效,但每次都会重复 RunExamplesOnce 和 RunExamplesForAverage 中的许多功能。 这些函数将始终采用示例,遍历它们 运行 一个始终 return bool Pass 和 long ElapsedMilliseconds 的函数,并用这些结果更新显示的文本。

protected override void RunExamplesOnce(CancellationToken token, IProgress<int> progress = null)
        {
            answer = "";

            List<bool> passesForEachExample = new List<bool>();
            List<long> timesElapsedForEachExample = new List<long>();

            for (int i = 0; i < examples.Length; i++)
            {
                // Stop if cancelled
                if (token.IsCancellationRequested)
                {
                    answer = $"{Name} cancelled\r\n";
                    return;
                }

                // Report Progress
                ReportProgress(i, examples.Length, progress);

                // Perform code
                var (Pass, ElapsedMilliseconds) = examples.ElementAt(i).TestExample();

                answer += $"Example {i}: Pass {Pass} in {ElapsedMilliseconds} ms\r\n";
                passesForEachExample.Add(Pass);
                timesElapsedForEachExample.Add(ElapsedMilliseconds);
            }

            ReportProgress(100, progress);
            answer += $"Overall Pass {passesForEachExample.All(c => c = true)} in {timesElapsedForEachExample.Sum()} ms\r\n\r\n";
        }

我的看法 - 该函数不关心输入或输出类型是什么。它只需要将一个 Example 传递给 TestExample() ,它将始终被适当地覆盖。 有没有什么方法可以在抽象 class 问题中使用未定义的泛型 Example (这似乎不起作用)来定义函数,或者改变我如何构造这些函数的方法 class是否可以达到相同的结果?


编辑:

感谢您的回答!我最终采纳了 Legacy Code 的建议,这非常适合我想做的事情。如果有人好奇,代码在我的 GitHub 上,现在位于 elysiara/TopCoder

如果 Problem class 包含示例,那么您可以在其中实现 RunExamplesOnce 并提供使用 virtual 更改该实现的能力。

抽象 classes 可以包含具有实现的方法,这样您就不会重复代码。

public abstract class Problem
{
    // Some public fields and tasks exist here
    // Some protected methods for reporting progress exist here
    // Some abstract fields for things like the problem name and the link to the problem page exist here

    protected virtual void RunExamplesOnce(CancellationToken token, IProgress<int> progress = null)
    {
        answer = "";

        List<bool> passesForEachExample = new List<bool>();
        List<long> timesElapsedForEachExample = new List<long>();

        for (int i = 0; i < examples.Length; i++)
        {
            // Stop if cancelled
            if (token.IsCancellationRequested)
            {
                answer = $"{Name} cancelled\r\n";
                return;
            }

            // Report Progress
            ReportProgress(i, examples.Length, progress);

            // Perform code
            var (Pass, ElapsedMilliseconds) = examples.ElementAt(i).TestExample();

            answer += $"Example {i}: Pass {Pass} in {ElapsedMilliseconds} ms\r\n";
            passesForEachExample.Add(Pass);
            timesElapsedForEachExample.Add(ElapsedMilliseconds);
        }

        ReportProgress(100, progress);
        answer += $"Overall Pass {passesForEachExample.All(c => c = true)} in {timesElapsedForEachExample.Sum()} ms\r\n\r\n";
    }

    protected abstract void RunExamplesForAverage(CancellationToken token, IProgress<int> progress = null);
}

RunExamplesForAverage也是如此。

话又说回来,如果你的抽象 class 真的没有任何它 children 需要实现的东西,你真的需要它吗?

为您的 example 摘要添加一个接口 class

protected abstract class ExampleBase<T1, T2> : IExample
{
    public T1 Inputs;
    public T2 CorrectOutput;

    public Example(T1 inputs, T2 correctOutput)
    {
        this.Inputs = inputs;
        this.CorrectOutput = correctOutput;
    }

    public abstract (bool Pass, long ElapsedMilliseconds) TestExample();
}

public interface IExample
{
        (bool Pass, long ElapsedMilliseconds) TestExample();
}

然后您必须将 examples array/list/collection 的元素类型更改为 IExample。 之后,您可以在 Problem 基础 class.

中使用 RunExamplesOnce 方法