【问题标题】:double[]=operator/(double[] a,double[] b) or Vector[]=operator/(Vector[] a, Vector[] b); Why can I not do this? Even just for myself? [closed]double[]=operator/(double[] a,double[] b) 或 Vector[]=operator/(Vector[] a, Vector[] b);为什么我不能这样做?哪怕只是为了我自己? [关闭]
【发布时间】:2016-06-12 16:00:28
【问题描述】:

我想重载数学和数组和 [] 来做数学运算

经过多次反复,答案是 c# 的设计不会让您像在 c++ 中那样为自己的“内置”类型重载。

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

旧的 c 程序员(不情愿地转向 c#)会要求这样做。我要求这个。

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

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

下面,我回答我的问题。 秘诀是在 C++ 中你可以重载 =/ TOKEN。 =/ 是 a=a/b 的缩写。运算符 =/ 是一个令牌。

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

...插入顶点类...


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 的包装器)。

【问题讨论】:

  • 第 1 步:struct VertexArray { public Vertex[] items; } 第 2 步:为 VertexArray 对象定义运算符。
  • 删除了 C++ 标签,因为您的问题是关于 C# 的(您没有显示任何 C++ 代码或问题)。
  • 什么是modern c#,而不是legacy c# ???
  • 现代 c# 版本 6。此处描述了旧版 msdn.microsoft.com/en-us/library/hh156499.aspx 版本 6 中的功能之一是重载解析。我不知道最终这是否是一个问题。

标签: c# math operator-overloading


【解决方案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);
}

【讨论】:

    【解决方案2】:

    我现在明白了这个问题。

    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# 在数学上数不胜数/文盲的指控。

    【讨论】:

      【解决方案3】:

      你有

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

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

      因此,绝不是“c# 在数学上是文盲/数不清”。只需一点点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());
      
          }
      }
      

      【讨论】:

      • 再次查看真实工作的 C# 代码。
      • 在这上面睡觉后我发现这个编译:float [] Centroid (Vertex [] v) { int Count = v.数数 ( );顶点 s = 新顶点(); foreach ( v 中的顶点 vv ) { s += vv; } 浮动 [ ] V = 新浮动 [ 3 ];返回 V; }
      • public static Vertex operator+ ( Vertex a , Vertex b ) { Vertex c = new Vertex(); C 。 x = 一个。 x + b 。 X; C 。 y = 一个。 y + b 。是; C 。 z = 一个。 z + b 。 z;返回 c; }
      • 好的。 “冗长”的解决方案。
      猜你喜欢
      • 2010-10-11
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多