参数化的类族

时间:2016-07-21 13:59:51

标签: c# class parameters static-methods

我想建立一个

类的家庭
  • 单独进行计算(即不需要类的实例)
  • 都进行相同的计算,但参数不同,参数硬编码到每个类中。

根据帖子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);
    }
}

然后我想定义子类

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

但它并不像我想要的那样工作,因为子类中的TOL值未在PrecisionSpace的静态方法中使用。因此无论我为T放置哪个子类T.TolEquals(...),编译器总是使用超类PrecisionSpace中定义的值(这是double的默认值,即{{ 1}})。

简而言之,0字段无法覆盖。那我该怎么办?

我可以删除所有static修饰符,并在派生类的构造函数中设置所需的static值。但后来我总是需要有类的实例。由于我不是真的需要实例,我可以让这些类单例化。但后来我必须确保它是线程安全的,等等......嗯。

还有什么?

我可以用与TOL相同的方式编写不相关的类,使用静态方法,用PrecisionSpace替换TOL0或我为每个类选择的值。但这会产生大量冗余代码,并且由于这些类没有共同的基类或接口,我无法正确定义泛型,如下所示:

1E-10

还有其他想法吗?与代表们有什么关系吗?

对于那些提出问题&#34;为什么&#34;:请阅读我在问题开头链接的主题。

3 个答案:

答案 0 :(得分:2)

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

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);
    }
}

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

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

答案 1 :(得分:0)

在基础.net框架中,你要做的最好的近似是Double,Int等系列,它们暴露相同的方法,但是对不同的数据类型进行操作并给出不同的结果。

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

答案 2 :(得分:0)

如果完全是语法上的便利而其他考虑并不重要,可以通过使用泛型类作为自己的参数来实现所需的效果,如下所示,允许您在静态方法中对其进行内部实例化,如下所示。

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);
    }
}

在派生类中,您将覆盖虚方法。

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));

然而,这需要一些代价。能够从继承中受益的类实例的生成对于调用者来说是不可见的,但它们仍然发生。此外,类实际上并不是从相同的基类派生的,因为它们使用不同的类型参数。