类 的参数化系列

Parametrized family of classes

我想建立一个 class 的家庭

基于线程 https://softwareengineering.stackexchange.com/q/325213/237600 ,我想到了这个:

public abstract class PrecisionSpace
{
    protected static double TOL; // This value is supposed to be
                                 // different in subclasses, so that...

    public static bool TolEquals(double left, double right) // ... this method, ...
    {
        return (Math.Abs(left - right) <= TOL);
    }

    public static bool TolSmaller(double left, double right) // ... this method and ...
    {
        return (left + TOL < right);
    }

    public static bool TolLarger(double left, double right) // ... this method behave
                                                            //     differently.
    {
        return TolSmaller(right, left);
    }
}

那我想定义subclasses

public class PerfectPrecision : PrecisionSpace
{
    (new) private static double 0; // doesn't matter if I put "new" in front or not
} 

public class MyPrecision: PrecisionSpace
{
    (new) private static double 1E-10;
}

并能够使用像

这样的语句
bool foo = MyPrecision.TolEquals(0, 1E-11); // supposed to be true

但它并没有像我想要的那样工作,因为子class中的TOL值没有在PrecisionSpace的静态方法中使用。所以无论我为 T.TolEquals(...) 设置哪个 subclass T,编译器总是使用 superclass PrecisionSpace 中定义的值(这是默认值对于 double,即 0).

简而言之,static 字段不能被覆盖。那我该怎么办?

我可以删除所有 static 修饰符并在派生的 classes 的构造函数中设置所需的 TOL 值。但是我总是需要 classes 的实例。因为我真的不需要实例,所以我可以使 classes 成为单例。但是我必须确保它是线程安全的等等......嗯。

还有什么?

我可以用与 PrecisionSpace 相同的方式编写不相关的 classes,使用静态方法,用 TOL 代替 01E-10 或其他我根据 class 选择的值。但这会产生很多冗余代码,并且由于 classes 没有公共基础 class 或接口,我无法正确定义泛型,如下所示:

public class MyVector<T> where T : (base class or interface of the Precision classes)

还有其他想法吗?也许与代表有关?

问"why"的人:请阅读我问题开头链接的主题。

在基础 .net 框架中,最接近您尝试做的事情的是 Double、Int 等系列 classes,它们公开相同的方法但对不同的数据类型进行操作并给出不同的结果。

如果功能相同(例如 ToString),框架会使用它调用的共享静态 class 来执行该功能。这样减少了编码量但是没有使用继承等方式来减少端点数量

继承不适用于静态成员。简而言之,你不能这样做。如果你想要多态行为,你将不得不创建实例。如果您的 classes 被构建为不可变的,线程安全根本不应该成为问题。

public abstract class PrecisionSpace
{
    private readonly double TOL;

    protected PrecisionSpace(double tol) { TOL = tol; }

    public bool TolEquals(double left, double right)
    {
        return (Math.Abs(left - right) <= TOL);
    }
}

public sealed class PerfectPrecision : PrecisionSpace
{
    public PerfectPrecision() : base(0) { }
}

internal static class PrecisionSpaceCache<T> where T : PrecisionSpace, new()
{
    public static readonly T Instance = new T();
}

public class MyVector<T> where T : PrecisionSpace, new()
{
    public void Foo(double x, double y)
    {
        PrecisionSpaceCache<T>.Instance.TolEquals(x, y);
    }
}

这样您就可以在其他 class 中使用 PrecisionSpaceCache(例如 Matrix)。此外,我会考虑将所有向量、矩阵等 classes 转换为值类型 (struct),如果你打算创建很多它们。

另一种选择是只在 Vector class 的构造函数中传递精度。

如果只是为了语法方便而其他考虑并不重要,您可以通过使用泛型 class 作为其自己的参数来达到预期的效果,如下所示,允许您在内部实例化它静态方法,如下

public class Precision<T> where T : Precision<T>, new()
{
    protected virtual double TOL
    {
        get { throw new NotImplementedException(); }
    }

    public static bool TolEquals(double left, double right)
    {
        return (Math.Abs(left - right) <= new T().TOL);
    }
}

在派生的 classes 中,您将覆盖虚拟方法。

public class PerfectPrecision : Precision<PerfectPrecision>
{
    protected override double TOL
    {
        get { return 0.00f; }
    }
}

public class MyPrecision : Precision<MyPrecision>
{
    protected override double TOL
    {
        get { return 0.01f; }
    }
}

这样达到了预期的效果,可以验证如下。

Console.WriteLine(PerfectPrecision.TolEquals(0.000f, 0.0001f));
Console.WriteLine(MyPrecision.TolEquals(0.000f, 0.0001f));

但是,这是有代价的。能够从继承中获益的 class 个实例的生成仅对调用者不可见,但它们仍然会发生。此外,classes 实际上并不派生自相同的基础 class,因为它们使用不同的类型参数。