double [] = operator /(double [] a,double [] b)或Vector [] = operator /(Vector [] a,Vector [] b);为什么我不这样做?甚至只为我自己?

时间:2016-06-12 16:00:29

标签: c# math operator-overloading

我想重载数学和数组以及[]进行数学运算

经过大量的反复尝试后,答案是c#的设计不会让你自己的“内置”类型超载,就像在c ++中一样。

这引起了很多争论,并且可能在未来作为一个特征出现。

老c程序员移动(不情愿地转向c#)将要求这样做。我要求这个。

例如,我有很多用于openGL的派生类型,例如Vertex x。我想添加它们,制作它们的数组,找到它们。将它们继承为更大的对象,如三角形或四边形条。

具体来说,我想为= / accumulator运算符重载二元运算符。

下面,我回答我的问题。 秘密在于C ++,你可以重载= / TOKEN。 = /是a = a / b的缩写。 operator = /是一个令牌。

在c#中它是两个令牌,你不能重载赋值(=)(使用隐式转换或显式转换),你重载 运算符作为二进制第二个标记。

例如:

class Vertex{
    public float x,y,z;
    public Vertex(){get;set}

    int some_small_int=2;
    Vertex[] A=new Vertex[some_small_int];
    Vertex[] B=new Vertex[some_small_int];
    Vertex[] C=new Vertex[some_small_int];

    public static Vertex[] operator+(Vertex[] A, Vertex[] B)
    {
        Vertex[] C=new Vertex[A.Count()];
        for( int i=0;i< A.Count();i++)
        {
            C[i]=A[i]+B[i];
        }
        return C;
        }
    }
}

...插入Vertex类......

array Vertex plus(array Vertex A, array Vertex B){
array Vertex C=new array<vertex>[A.Count()]; // B.Count() better be the same.
    for(int i=0;i<A.Count();i++)
    {
        C[i].x=A[i].x+B[i].x;
        C[i].y=A[i].y+B[i].y;
        C[i].z=A[i].z+B[i].z;
    }
}

为什么不能在c#中执行此操作?

因为它是这样设计的。我必须写一个类Float(作为float的包装器)。

3 个答案:

答案 0 :(得分:1)

以下是Vector3类的完整列表,以便了解如何实现运算符和索引器。

[ImmutableObject(true)]
public struct Vector3 : IEnumerable<float>, ICloneable
{
    readonly float x, y, z;

    #region Definition
    public Vector3(float x, float y, float z)
    {
        this.x=x;
        this.y=y;
        this.z=z;
    }
    public Vector3(double x, double y, double z)
        : this((float)x, (float)y, (float)z) { }

    public Vector3(Vector3 other)
    {
        this.x=other.x;
        this.y=other.y;
        this.z=other.z;
    }
    public Vector3(string description)
        : this()
    {
        FromString(description);
    }
    /// <summary>
    /// Indexer allows the use of the '[]' operator in Vector3
    /// </summary>
    /// <param name="index">The integer index 0-2</param>
    /// <returns>A scalar value</returns>
    public float this[int index]
    {
        get
        {
            switch (index)
            {
                case 0: return this.x;
                case 1: return this.y;
                case 2: return this.z;  
            }
            throw new IndexOutOfRangeException();
        }
    }
    public float X { get { return x; } }
    public float Y { get { return y; } }
    public float Z { get { return z; } }
    public float Magnitude { get { return Norm(); } }
    public float Norm() { return (float)Math.Sqrt(x*x+y*y+z*z); }
    public Vector3 Normalized() { var m=Norm(); if (m>0) return this/m; return this; }
    public static readonly Vector3 O=new Vector3(0, 0, 0);
    public static readonly Vector3 I=new Vector3(1, 0, 0);
    public static readonly Vector3 J=new Vector3(0, 1, 0);
    public static readonly Vector3 K=new Vector3(0, 0, 1);
    public static explicit operator float[](Vector3 vector)
    {
        return vector.ToArray();
    }
    #endregion

    #region Math
    public Vector3 Add(Vector3 other, float scale=1)
    {
        return new Vector3(
            x+scale*other.x,
            y+scale*other.y,
            z+scale*other.z);
    }
    public Vector3 Scale(float scale)
    {
        return new Vector3(
            scale*x,
            scale*y,
            scale*z);
    }
    public Vector3 Multiply(Matrix3 rhs)
    {
        return new Vector3(
            X*rhs.A11+Y*rhs.A12+Z*rhs.A13,
            X*rhs.A21+Y*rhs.A22+Z*rhs.A23,
            X*rhs.A31+Y*rhs.A32+Z*rhs.A33);
    }
    public Vector3 Reciprocal(float numerator)
    {
        return new Vector3(numerator/x, numerator/y, numerator/z);
    }
    public static float Dot(Vector3 v1, Vector3 v2)
    {
        return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z;
    }
    public static Vector3 Cross(Vector3 v1, Vector3 v2)
    {
        return new Vector3(
            v1.y*v2.z-v1.z*v2.y,
            v1.z*v2.x-v1.x*v2.z,
            v1.x*v2.y-v1.y*v2.x);
    }
    public static float AngleBetween(Vector3 v1, Vector3 v2)
    {
        var cos=Dot(v1, v2);
        var sin=Cross(v1, v2).Norm();
        return (float)Math.Atan2(sin, cos);
    }
    public Vector3 AlongX() { return new Vector3(x, 0, 0); }
    public Vector3 AlongY() { return new Vector3(0, y, 0); }
    public Vector3 AlongZ() { return new Vector3(0, 0, z); }
    public Vector3 AlongXY() { return new Vector3(x, y, 0); }
    public Vector3 AlongYZ() { return new Vector3(0, y, z); }
    public Vector3 AlongZX() { return new Vector3(x, 0, z); }

    public Vector3 RotateAbout(Vector3 axis, float angle)
    {
        return Matrix3.RotateAbout(axis, angle)*this;
    }

    public Vector3 RotateAboutX(float angle)
    {
        float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);

        return new Vector3(
            x,
            y*cos-z*sin,
            y*sin+z*cos);
    }
    public Vector3 RotateAboutY(float angle)
    {
        float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);

        return new Vector3(
            x*cos+z*sin,
            y,
            -x*sin+z*cos);
    }
    public Vector3 RotateAboutZ(float angle)
    {
        float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);

        return new Vector3(
            x*cos-y*sin,
            x*sin+y*cos,
            z);
    }
    public Vector3 MirrorAboutXY() { return new Vector3(x, y, -z); }
    public Vector3 MirrorAboutXZ() { return new Vector3(x, -y, z); }
    public Vector3 MirrorAboutYZ() { return new Vector3(-x, y, z); }
    #endregion

    #region Operators
    public static Vector3 operator+(Vector3 lhs, Vector3 rhs) { return lhs.Add(rhs); }
    public static Vector3 operator-(Vector3 rhs) { return rhs.Scale(-1); }
    public static Vector3 operator-(Vector3 lhs, Vector3 rhs) { return lhs.Add(rhs, -1); }
    public static Vector3 operator*(float lhs, Vector3 rhs) { return rhs.Scale(lhs); }
    public static Vector3 operator*(Vector3 lhs, float rhs) { return lhs.Scale(rhs); }
    public static Vector3 operator/(Vector3 lhs, float rhs) { return lhs.Scale(1/rhs); }
    public static Vector3 operator/(float lhs, Vector3 rhs) { return rhs.Reciprocal(lhs); }
    public static float operator*(Vector3 lhs, Vector3 rhs) { return Dot(lhs, rhs); }
    public static Vector3 operator^(Vector3 lhs, Vector3 rhs) { return Cross(lhs, rhs); }
    public static Vector3 operator*(Vector3 lhs, Matrix3 rhs)
    {
        return lhs.Multiply(rhs);
    }
    #endregion

    #region ICloneable Members

    public Vector3 Clone() { return new Vector3(this); }

    object ICloneable.Clone()
    {
        return Clone();
    }

    #endregion

    #region IEnumerable<float> Members

    public IEnumerator<float> GetEnumerator()
    {
        yield return x;
        yield return y;
        yield return z;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion


    #region IEquatable Members

    /// <summary>
    /// Equality overrides from <see cref="System.Object"/>
    /// </summary>
    /// <param name="obj">The object to compare this with</param>
    /// <returns>False if object is a different type, otherwise it calls <code>Equals(Vector3)</code></returns>
    public override bool Equals(object obj)
    {
        if (obj is Vector3)
        {
            return Equals((Vector3)obj);
        }
        return false;
    }

    /// <summary>
    /// Checks for equality among <see cref="Vector3"/> classes
    /// </summary>
    /// <param name="other">The other <see cref="Vector3"/> to compare it to</param>
    /// <returns>True if equal</returns>
    public bool Equals(Vector3 other)
    {
        return x.Equals(other.x)
            &&y.Equals(other.y)
            &&z.Equals(other.z);
    }

    /// <summary>
    /// Calculates the hash code for the <see cref="Vector3"/>
    /// </summary>
    /// <returns>The int hash value</returns>
    public override int GetHashCode()
    {
        unchecked
        {
            return ((17*23+x.GetHashCode())*23+y.GetHashCode())*23+z.GetHashCode();
        }
    }

    #endregion

    #region IFormattable Members
    public override string ToString()
    {
        return ToString("G");
    }
    public string ToString(string format)
    {
        return ToString(format, CultureInfo.CurrentCulture.NumberFormat);
    }
    public string ToString(string format, IFormatProvider formatProvider)
    {
        return string.Format("({0},{1},{2})",
            x.ToString(format, formatProvider),
            y.ToString(format, formatProvider),
            z.ToString(format, formatProvider));
    }

    #endregion

    #region Triangles
    public static float TriangleArea(Vector3 a, Vector3 b, Vector3 c)
    {
        Vector3 u=b-a, v=c-a;
        Vector3 k=Vector3.Cross(u, v);
        return k.Magnitude/2;
    }

    public static Vector3 TriangleNormal(Vector3 a, Vector3 b, Vector3 c)
    {
        Vector3 u=b-a, v=c-a;
        return Vector3.Cross(u, v).Normalized();
    }

    #endregion


    #region IParsable Members

    public void FromString(string description)
    {
        // "(X,Y,Z)" => (X,Y,Z)
        description=description.Trim('(', ')');
        var parts=description.Split(',');
        if (parts.Length==3)
        {
            float new_x=0, new_y=0, new_z=0;
            if (!float.TryParse(parts[0].Trim(), out new_x))
            {
                new_x=x;
            }
            if (!float.TryParse(parts[1].Trim(), out new_y))
            {
                new_y=y;
            }
            if (!float.TryParse(parts[2].Trim(), out new_z))
            {
                new_z=z;
            }
            this=new Vector3(new_x, new_y, new_z);
        }
    }

    public float[] ToArray()
    {
        return new float[] { x, y, z };
    }


    #endregion

}

这里的一些示例用法:

public TestVector()
{
    Vector3 A=new Vector3(1, 2, 3);
    Vector3[] array=new Vector3[100];
    array[0]=A;
    for (int i=1; i<100; i++)
    {                
        array[i]=2*array[i-1]+Vector3.Cross(array[i], Vector3.I);
        // or 2*array[i-1]+(array[i]^Vector3.I);
    }
    float Ax = A[0];
    float max_x=array.Max((v) => v.X);
}

答案 1 :(得分:0)

你有

public static Vertex[] operator+(Vertex[] A, Vertex[] B)
{
    return new vertex[] { A.x+B.x, A.y+B.y, A.z+B.z };
}

return new vertex[] { A.x+B.x, A.y+B.y, A.z+B.z };

不是有效表达式,因为Vertex[] A没有A.x

快速lambda解决方案:

return A.Zip(B, (v1, v2) => new Vertex(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z)).ToArray();

如果重载new Vertex()对象的+运算符,后面的Vertex表达式也可以简化。

所以,绝不是&#34; c#是数学文盲/无数&#34;。只需要一点点System.Linq就可以使它成为一个很好的表达。

这基本上是在C#中使用运算符重载实现的分量向量加法。另请参阅http://www.dotnetperls.com/zip

编辑:不,实际上我错了。您只能从封闭类重载运算符,因此除非您将Vertex[]声明为自己的包装类,否则不会直接重载Vertex[]。但这里有一个完整的,有效的代码,带有一些运算符重载和向量加法。

using System.IO;
using System;
using System.Linq;

class Vertex
{
   public float x,y,z;

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

    public static Vertex operator+(Vertex v1, Vertex v2)
    {
        return new Vertex(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z);
    }

    public static Vertex[] AddVertices(Vertex[] A, Vertex[] B)
    {
        return A.Zip(B, (v1, v2) => v1 + v2).ToArray();
    }

    public override String ToString() { return string.Format("({0}, {1}, {2})",x,y,z);}
}


class Program
{
    const int some_small_int=2;
    static Vertex[] A=new Vertex[]{ new Vertex(10.0f, 10.0f, 10.0f),new Vertex(10.0f, 10.0f, 10.0f)};
    static Vertex[] B=new Vertex[]{new Vertex(10.0f, 10.0f, 10.0f),new Vertex(10.0f, 10.0f, 10.0f)};

    static Vertex[] C= Vertex.AddVertices(A,B);


    static void Main()
    {
        Console.WriteLine("Vertex Array A:");
        foreach(var vert in A) Console.WriteLine(vert);

        Console.WriteLine("Vertex Array B:");
        foreach(var vert in B) Console.WriteLine(vert);

        Console.WriteLine("Vertex Array C:");
        foreach(var vert in C) Console.WriteLine(vert);

        var vert1 = new Vertex(1,2,3);
        var vert2 = new Vertex(4,5,6);
        var vert3 = vert1 + vert2;

        Console.WriteLine("Vertex v1 + v2:" + vert3.ToString());

    }
}

答案 2 :(得分:0)

我现在明白了这个问题。

执行c#(=)赋值然后(+)添加为两个运算符,而不是单个运算符(= +)求和。

在c ++(=)中是一个令牌,它是一个可以重载的一元运算符。

在c ++中 a = + b是a = a + b

的简写

在c#

a = + b扩展为a = a + b但它不是可以重载的相等运算符,而是加法运算符作为二元运算符。

因此,重载的解决方案是为二元运算符所需的类型重载加号,减号,乘法,除法等,而不是一元运算符。

令人惊讶的是,这似乎是为了计算Boxel类型的质心而编译的,它由一组边缘组成,每条边有两个顶点。我还没有测试运行时代码,但认为它现在可以正常工作。

        public static Vertex operator / ( Vertex a , int b )
            {
            Vertex c = new Vertex ( );
            c . x = a . x / b;
            c . y = a . y / b;
            c . z = a . z / b;
            return c;
            }
        public static Vertex operator + ( Vertex a , Vertex b )
            {
            Vertex c = new Vertex ( );
            c . x = a . x + b . x;
            c . y = a . y + b . y;
            c . z = a . z + b . z;
            return c;
            }



        Vertex NewCentroid ( Boxel B )
        {
        Vertex C = new Vertex();

        C = NewCentroid ( B . E );

        return C;
        }
    Vertex NewCentroid ( Edge [ ] E )
        {
        Vertex C = new Vertex ( ){0.0,0.0,0.0};

        foreach ( Edge e in E )
            {
            C **+** = NewCentroid ( e );
            }

        return C;
        }
    Vertex NewCentroid ( Edge e )
        {
        Vertex C = new Vertex ( );
        C = NewCentroid ( e . V0 , e . V1 );
        return C;
        }
    Vertex NewCentroid ( Vertex v0 , Vertex v1 )
        {
        Vertex C = new Vertex ( );

        C = v0 **+** v1;
        C =**/** 2.0;
        return C;
        }

如果我错了,请纠正我。在我的编程方式中,我老了,衰老了。

Cap Sigma是大希腊字母,通常用作从楼下标到楼上标的总和。

现在有了象征意识,老了,破旧我很开心。

我撤回了关于c#在数学上是无数/文盲的指责。