我想重载数学和数组以及[]进行数学运算
经过大量的反复尝试后,答案是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的包装器)。
答案 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#在数学上是无数/文盲的指责。