在编译时矢量数学维度一致性检查

时间:2014-12-03 21:29:46

标签: c#

我在C#中创建一个线性代数库,我想强制维度不一致错误到编译时。我已经为this实现了一个类似的解决方案,我使用的特性是一个唯一映射到整数的类。问题是我希望我的Vector有各种可能的大小,我需要创建一个具有唯一名称的类来表示它。

以下是该实施的一个示例:

public class Vector<T> where T: ISize, new()
{
    static readonly T size = new T();
    List<double> values;

    public Vector(List<double> values)
    {
        if (values.Count != size.Size)
            throw new IndexOutOfRangeException();

        this.values = new List<double>(values);
    }

    public double Get(int index)
    {
        return values[index];
    }

    public Vector<T> Add(Vector<T> other)
    {
        var vv = new List<double>();

        for (int ii = 0; ii < size.Size; ++ii)
            vv.Add(other.Get(ii) + this.values[ii]);

        return new Vector<T>(vv);
    }
}

public interface ISize
{
    int Size { get; }
}

public class S1 : ISize
{
    public int Size
    {
        get { return 1; }
    }
}

public class S2 : ISize
{
    public int Size
    {
        get { return 2; }
    }
}

以下是其用法示例:

class Program
{
    static void Main(string[] args)
    {
        var v1 = new Vector<S2>(new List<double>() { 1, 2 });
        var v2 = new Vector<S2>(new List<double>() { 10, -4 });
        var z1 = new Vector<S1>(new List<double>() { 10 });

        // works
        var v3 = v1.Add(v2);

        // complie-time error
        var z2 = z1.Add(v1);
    }
}

这对我的目的非常有效,除了我需要为每个可能的Vector大小创建不同的ISize实现。有没有办法让我实现允许我解决这个问题的Vector类?

2 个答案:

答案 0 :(得分:1)

为了获得编译时错误,您需要具有不同的类型。 C#没有一个概念让你定义一个类型参数,它本身就是一种值参数 - 这就是你需要做的事情。

因此,我不认为你的要求是可能的。

我认为可能有一种方法可以使用匿名类型为矢量实例族创建唯一类型,但这将是古怪的,我认为它不会提供您想要的类型安全性。

C ++在模板中有这样一个概念(所以这不是不合理的),在C#中是不可能的。

答案 1 :(得分:0)

您可以使用编译时类型检查创建单个N维的Vector类,但它非常混乱。我们在这里创建的是LISP样式链接列表,但是通过泛型类型参数而不是纯粹通过字段引用的对象引用。

public interface IVector
{
    double Value { get; }
    IVector Tail { get; }
}
public class Vector<T> : IVector
    where T : IVector
{
    internal Vector(double value, T tail)
    {
        Value = value;
        Tail = tail;
    }

    public double Value { get; private set; }
    public T Tail { get; private set; }

    public Vector<Vector<T>> Add(double value)
    {
        return new Vector<Vector<T>>(value, this);
    }
}
internal class EmptyVector : IVector
{
    public double Value
    {
        get { throw new NotImplementedException(); }
    }

    public IVector Tail
    {
        get { return null; }
    }
}

public static class Vector
{
    public static readonly Vector<IVector> Empty = new Vector<IVector>(
        0, new EmptyVector());
    public static IEnumerable<double> AllValues(this IVector vector)
    {
        IVector current = vector;
        while (current != Vector.Empty && current != null)
        {
            yield return current.Value;
            current = current.Tail;
        };
    }
}

这允许我们写:

var v1 = Vector.Empty.Add(1).Add(2);
var v2 = Vector.Empty.Add(10).Add(-4);
var z1 = Vector.Empty.Add(10);


v1 = v2;//works, as they are the same type
z1 = v2;//fails, as they aren't the same type, since they're a different size

这允许您编写一个接受特定大小的矢量的方法。它不方便,并且不能扩展,但它可以工作。如果你想要一个3D矢量作为参数,你可以写:

public static void Foo(Vector<Vector<Vector<IVector>>> vector)
{
    var first = vector.Value;
    var second = vector.Tail.Value;
    var third = vector.Tail.Tail.Value;
}