简体   繁体   English

我应该如何在Vector3结构中实现.Equals()?

[英]How should I implement .Equals() in my Vector3 structure?

I have an immutable Vector3 structure, and I'm wondering how to best implement the .Equals() method so that it's useful and still satisfies the Guidelines for Overloading Equals() . 我有一个不变的Vector3结构,我想知道如何最好地实现.Equals()方法,以便它有用并且仍然满足Equals()重载准则

Here's a partial implementation of my structure: 这是我的结构的部分实现:

public struct Vector3
{
    private float _x;
    private float _y;
    private float _z;

    public float X { get { return _x; }} 
    public float Y { get { return _y; }}
    public float Z { get { return _z; } }

    public Vector3(float x, float y, float z)
    {
        _x = x;
        _y = y;
        _z = z;
    }

    public override bool Equals(object obj)
    {
        //What should go here?
    }
}

Edit: I don't want to directly compare each X, Y, Z due to the nature of floating point operations. 编辑:由于浮点运算的性质,我不想直接比较每个X,Y,Z。 For example, I can tell if two vector3s are parallel by checking to see if u x v == <0, 0, 0>; 例如,我可以通过检查u x v == <0,0,0>来判断两个vector3是否平行。 however with floating point operations this will often fail because one of the zeros is actually <8.205348E-09>. 但是,对于浮点运算,这通常会失败,因为零之一实际上是<8.205348E-09>。

I'm looking for an Equals() method that is a bit smarter. 我正在寻找一个更聪明的Equals()方法。 I want it to work whether the numbers are very large or very small. 无论数字太大还是很小,我都希望它起作用。 I 一世

In NGenerics http://code.google.com/p/ngenerics/ we have a base class VectorBase 在NGenerics http://code.google.com/p/ngenerics/中,我们有一个基类VectorBase

From VectorBase 从VectorBase

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }

        var vector = obj as IVector<T>;
        return (EqualsInternal(vector));
    }

    public bool Equals(IVector<T> other)
    {
        return other != null && EqualsInternal(other);
    }


    private bool EqualsInternal(IVector<T> other)
    {

        if (dimensionCount != other.DimensionCount)
        {
            return false;
        }
        for (var i = 0; i < dimensionCount; i++)
        {
            if (!Equals(this[i], other[i]))
            {
                return false;
            }
        }
        return true;
    }

    public static bool operator ==(VectorBase<T> left, IVector<T> right)
    {
        // If both are null, or both are same instance, return true.
        if (ReferenceEquals(left, right))
        {
            return true;
        }

        // If one is null, but not both, return <c>false</c>.
        if (((object)left == null) || (right == null))
        {
            return false;
        }

        // Return true if the fields match:
        return left.EqualsInternal(right);
    }

    public static bool operator !=(VectorBase<T> left, IVector<T> right)
    {
        return !(left == right);
    }


    /// <inheritdoc />
    public override int GetHashCode()
    {
        var hashCode = 0;
        for (var index = 0; index < dimensionCount; index++)
        {
            hashCode ^= this[index].GetHashCode();
        }
        return hashCode;
    }

Then we have a Vector3D which inherits from VectorBase< 然后我们有一个Vector3D,它继承自VectorBase <

class Vector3D : VectorBase<double>

The full list of vectors is 向量的完整列表是

* Vector2D - 2 dimensional vector.
* Vector3D - 3 dimensional vector.
* VectorN - A vector with user-defined dimensions. 

http://code.google.com/p/ngenerics/wiki/Vectors http://code.google.com/p/ngenerics/wiki/Vectors

What is wrong with the following? 以下是什么问题?

protected const float EqualityVariance = 0.0000001;

public override bool Equals(object obj)
{
    Vector3? vector = obj as Vector3?;
    if (vector == null) return false;

    return Equals((Vector3)vector);
}

public bool Equals(Vector3 vector)
{
    return Math.Abs(this._x - vector._x) < EqualityVariance && 
           Math.Abs(this._y - vector._y) < EqualityVariance && 
           Math.Abs(this._z - vector._z) < EqualityVariance;
}

Unless there is some minimum-precision between vectors that is implied with the Vector3, I would make Equals be a simple FP equality check. 除非Vector3暗示矢量之间存在一些最小精度,否则我将使Equals成为简单的FP相等性检查。

In addition I would create an AlmostEquals method (possibly even an overloaded Equals which is free from the contract of Equals). 另外,我将创建一个AlmostEquals方法(甚至可能是重载的Equals,它没有Equals的约定)。 I would not put "fuzzy logic" into Equals because Equals as a strict contract with GetHashCode. 我不会在Equals中放入“模糊逻辑”,因为Equals是与GetHashCode的严格合同。

On the other hand, if a minimum-precision requirement is set, that could be used and would be just a normal equality check of the normalization. 另一方面,如果设置了最小精度要求,则可以使用该要求,这仅仅是对规范化的正常等式检查。 The reason to do it after normalization would be so that GetHashCode could also used the normalized values. 归一化之后这样做的原因是,GetHashCode也可以使用归一化的值。 (Just delta-checks across the values would not work for GetHashCode). (仅对值进行增量检查不适用于GetHashCode)。 I'm not a fan of this approach though. 我不是这种方法的粉丝。

Of course the approach used should take into account how the Vector3 is to be used and what role it has. 当然,要使用使用应考虑的Vector3是怎样的方法,它有什么作用。 Perhaps there is a need for a more restricted type? 也许需要一个更严格的类型?

You shouldn't implement Equals in Vector3 by using distance checks. 您不应该通过使用距离检查在Vector3中实现等于。 First of all this will break "if (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true." 首先, 这将打破“如果(x.Equals(y)&& y.Equals(z))返回true,则x.Equals(z)返回true。” contract . 合同 Do you need an example for this?. 您是否需要一个示例?
Second, why do you need to overload Equals method? 其次,为什么需要重载Equals方法? Who will be the client of that API? 谁将成为该API的客户端? Do you want to compare these objects by yourself or you want to use some libraries like collections or maps with instances of Vector3 as values and/or keys? 您是想自己比较这些对象,还是要使用集合或映射之类的带有Vector3实例的库作为值和/或键? In later case this can be very dangerous! 在以后的情况下,这可能非常危险! If you want to use such vectors as keys in hash maps when you will also need to define trivial GetHashCode method which will return constant. 如果要在哈希映射中将这样的向量用作键,则还需要定义简单的GetHashCode方法,该方法将返回常量。 So the best comparison will be "this._x == vector._x && this._y == vector._y && this._z == vector._z" in that case. 因此,在这种情况下,最好的比较是“ this._x == vector._x && this._y == vector._y && this._z == vector._z”。
If you want to have such comparison for yourself then it will be more clear if you will define some other method like "IsClose" with explanation of comparison idea in method summary. 如果您想自己进行比较,那么如果定义其他方法(例如“ IsClose”)并在方法摘要中对比较思路进行了说明, 将会更加清楚

"Would normalizing both vectors and then comparing each float within a small epsilon value work well?" “将两个向量归一化,然后在一个较小的ε值内比较每个浮点是否能很好地工作?”

Yes it should work quite well. 是的,它应该运作良好。 However there is better (more efficient) way to do this. 但是,有更好(更有效)的方法来执行此操作。 Extract exponents of compared numbers, then compare subtracted value to eps rised to the max exponents power. 提取比较数字的指数,然后将减去的值与eps上升到最大指数乘方进行比较。

Pseudo code would look like this: 伪代码如下所示:

exp1 = exponentOf(r1)
exp2 = exponentOf(r2)
if absolute_value(r2 - r1) <= epsilon^(max(exp1, exp2)) 
    return equal 
else
    return not_equal

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM