强制 classes 到 contain/use 他们自己的抽象具体实现 class

Force classes to contain/use their own specific implementation of an abstract class

我正在用 C# 开发一个 TUI 库,我需要有关如何为显示 object 制作颜色主题的建议。 Objects可以在屏幕上绘制的都继承自这个接口:

public interface IDrawable
    {
        Area ScreenArea { get; }
        List<char[]> DisplayChars { get; }
        //some other properties...

    }

或者更具体地说,每个可绘制对象的接口 object 实现了这个接口(IWindow 是一个 IDrawable)。每个 IDrawable 都绘制在由 Area 结构表示的控制台 window 的指定部分:

public struct Area
    {
        public readonly int EndX;
        public readonly int EndY;
        public readonly int Height;
        public readonly int StartX;
        public readonly int StartY;
        public readonly int Width;

        public Area(int startX, int endX, int startY, int endY)
        {
            StartX = startX;
            EndX = endX;
            StartY = startY;
            EndY = endY;
            Height = endY - startY;
            Width = endX - startX;
        }

        /// <summary>
        /// Get the overlapping area between this area and another.
        /// </summary>
        /// <param name="refArea"></param>
        /// <returns>Overlap area relative to the upper left corner of the ref area.</returns>
        public Area OverlapWith(Area refArea)
        {
            //....
        }
    }

objects 的实际绘制由静态 Display class 中的方法处理,这些方法在 DisplayChars 中的每个元素上调用 Console.Write()。我希望每个从 IDrawable 继承的 class 都被迫实施自己的规则,以将其区域划分为不同的颜色区域,例如,弹出窗口 windows 可能有为其外边框、标题(在外边框内)和内部区域单独着色。

一段时间以来,我一直在思考如何做到这一点。我需要创建一个类型 ColorScheme,以包含用什么颜色书写什么字符的规则。我决定最好的方法是将其设为抽象 class,其中包含可以单独应用颜色的 "sub-areas" 列表。

我希望每个 non-abstract IDrawable 都必须实现自己的 class 继承自 ColorScheme。例如,抽象 Window : IWindow class 将没有这样的实现,但 PopupWindow : Window class 必须具有相应的 PopupWindowColorScheme : ColorScheme 类型,其中作者PopupWindow 将定义如何将 class' Area 拆分为单独的区域。每个 PopupWindow 都有自己的这种类型的实例来包含其特定的颜色。

这可能吗?如果不是,是否有另一种方法可以强制 IDrawable 类型的作者指定一种将其区域拆分为可着色区域的方法?

您不能强制每个 IDrawable 都具有 唯一的 ColorScheme 实现(例如 IDrawable 的多个不同实现可以使用PopupWindowColorScheme)。但是,您可以使用 generic type constraints 添加实现接口的额外要求,如下所示:

public interface IDrawable<TColorScheme> 
    where TColorScheme : ColorScheme
{
    Area ScreenArea { get; }
    List<char[]> DisplayChars { get; }
    //some other properties...
    TColorScheme ColorScheme { get; }
}

现在,IDrawable 的每个实现都需要指定要使用的 ColorScheme 类型。但是消费者可以只实施 IDrawable<ColorScheme> 哪种方式违背了目的(取决于您的要求)。我们可以更进一步:

public interface IDrawable<TColorScheme> 
    where TColorScheme : ColorScheme, new()
{
}

public abstract class ColorScheme {  }

这里,由于ColorScheme是抽象的,泛型类型约束需要提供的类型参数来实现一个无参构造器(new()),所以ColorScheme本身不能作为范围。任何实现 class 都需要指定 ColorScheme 的自定义实现,它提供 public、无参数构造函数。

但我们可以走得更远:

public interface IDrawable { } 
public interface IDrawable<TDrawable, TColorScheme> : IDrawable
    where TDrawable : IDrawable, new()
    where TColorScheme : ColorScheme<TDrawable>, new()
{
    object ScreenArea { get; }
    List<char[]> DisplayChars { get; }
    //some other properties...
    TColorScheme ColorScheme { get; }
}

public abstract class ColorScheme<TDrawable>
    where TDrawable : IDrawable, new()
{
}

在这里,IDrawable 的每个实现都必须指定 ColorScheme 它使用什么 每个 ColorScheme 还必须指定 IDrawable 它适用于什么。而且因为每个都需要一个无参数的构造函数,所以它们都不能指定公共基类型。现在实现这个看起来有点奇怪:

public class MyDrawable : IDrawable<MyDrawable, MyColorScheme> { }

public class MyColorScheme : ColorScheme<MyDrawable> { }

仍然可以实现可重复使用的 ColorSchemeIDrawable,(例如 MyOtherDrawable : MyDrawable 使用 MyColorScheme)。然而,在我看来,这开始变得相当繁琐和乏味。一般来说,除非你有技术原因 使用类型约束,否则我会避免使用它,因为你经常会发现它在未来太受限制了。