Mat*_*zen 109 .net c# operator-overloading .net-4.0 c#-4.0
我试图做操作符重载+=,但我不能.我只能让操作员超载+.
怎么会?
编辑
这不起作用的原因是我有一个Vector类(带有X和Y字段).请考虑以下示例.
vector1 += vector2;
Run Code Online (Sandbox Code Playgroud)
如果我的运算符重载设置为:
public static Vector operator +(Vector left, Vector right)
{
return new Vector(right.x + left.x, right.y + left.y);
}
Run Code Online (Sandbox Code Playgroud)
然后结果将不会添加到vector1,而是vector1也将通过引用成为全新的Vector.
VMA*_*Atm 144
来自MSDN的可重载运算符:
赋值运算符不能过载,但是
+=,例如,使用+可以重载的方式进行评估.
更重要的是,任何赋值运算符都不能超载.我认为这是因为垃圾收集和内存管理会产生影响,这是CLR强类型世界中潜在的安全漏洞.
不过,让我们看看究竟是什么运营商.根据着名的Jeffrey Richter的书,每种编程语言都有自己的运算符列表,这些列表是在特殊的方法调用中编译的,而CLR本身对运算符一无所知.那么让我们看看究竟是什么+和+=运营商背后.
看到这个简单的代码:
Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);
Run Code Online (Sandbox Code Playgroud)
让我们查看IL-code以获取此说明:
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: ldc.i4.s 10
IL_000c: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0011: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
valuetype [mscorlib]System.Decimal)
IL_0016: stloc.0
Run Code Online (Sandbox Code Playgroud)
现在让我们看看这段代码:
Decimal d1 = 10M;
d1 += 10M;
Console.WriteLine(d1);
Run Code Online (Sandbox Code Playgroud)
和IL代码:
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: ldc.i4.s 10
IL_000c: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0011: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
valuetype [mscorlib]System.Decimal)
IL_0016: stloc.0
Run Code Online (Sandbox Code Playgroud)
他们是平等的!因此,+=运算符只是C#中程序的语法糖,你可以简单地重载+运算符.
例如:
class Foo
{
private int c1;
public Foo(int c11)
{
c1 = c11;
}
public static Foo operator +(Foo c1, Foo x)
{
return new Foo(c1.c1 + x.c1);
}
}
static void Main(string[] args)
{
Foo d1 = new Foo (10);
Foo d2 = new Foo(11);
d2 += d1;
}
Run Code Online (Sandbox Code Playgroud)
此代码将被编译并成功运行为:
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: newobj instance void ConsoleApplication2.Program/Foo::.ctor(int32)
IL_0008: stloc.0
IL_0009: ldc.i4.s 11
IL_000b: newobj instance void ConsoleApplication2.Program/Foo::.ctor(int32)
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: ldloc.0
IL_0013: call class ConsoleApplication2.Program/Foo ConsoleApplication2.Program/Foo::op_Addition(class ConsoleApplication2.Program/Foo,
class ConsoleApplication2.Program/Foo)
IL_0018: stloc.1
Run Code Online (Sandbox Code Playgroud)
更新:
根据您的更新 - 正如@EricLippert所说,您确实应该将向量作为不可变对象.添加两个向量的结果是一个新的向量,而不是第一个具有不同大小的向量.
如果由于某种原因你需要改变第一个向量,你可以使用这个重载(但对我来说,这是非常奇怪的行为):
public static Vector operator +(Vector left, Vector right)
{
left.x += right.x;
left.y += right.y;
return left;
}
Run Code Online (Sandbox Code Playgroud)
ben*_*ado 16
你不能超载,+=因为它不是一个独特的操作符,它只是语法糖.x += y只是写作的简写方式x = x + y.因为+=是根据+和=运算符定义的,所以允许你单独覆盖它可能会产生问题,在这种情况下,x += y并且x = x + y行为方式完全相同.
在较低级别,C#编译器很可能将两个表达式编译为相同的字节码,这意味着运行时很可能在程序执行期间不能对它们进行不同的处理.
我可以理解你可能想把它当作一个单独的操作:在x += 10你知道你可以改变x对象的语句中并且可能节省一些时间/内存,而不是x + 10在通过旧引用分配它之前创建一个新对象.
但请考虑以下代码:
a = ...
b = a;
a += 10;
Run Code Online (Sandbox Code Playgroud)
应该a == b到底?对于大多数类型,不,a是10多个b.但是如果你可以让+=运算符超载到位,那么是的.现在考虑一下a,b可以传递给程序的远端部分.如果您的对象在代码不期望的位置开始更改,那么您可能的优化可能会产生令人困惑的错误.
换句话说,如果性能非常重要,那么x += 10用类似的方法调用替换它并不困难x.increaseBy(10),并且对于所涉及的每个人来说都更加清晰.
age*_*t-j 15
这是因为赋值运算符不能重载的原因相同.您无法编写能够正确执行赋值的代码.
class Foo
{
// Won't compile.
public static Foo operator= (Foo c1, int x)
{
// duh... what do I do here? I can't change the reference of c1.
}
}
Run Code Online (Sandbox Code Playgroud)
赋值运算符不能重载,但是+ =,例如,使用+来计算,可以重载.
来自MSDN.
如果你+像这样重载运算符:
class Foo
{
public static Foo operator + (Foo c1, int x)
{
// implementation
}
}
Run Code Online (Sandbox Code Playgroud)
你可以做
Foo foo = new Foo();
foo += 10;
Run Code Online (Sandbox Code Playgroud)
要么
foo = foo + 10;
Run Code Online (Sandbox Code Playgroud)
这将编译并运行相同.
对于这个问题总有相同的答案:+=如果你超载,你为什么需要它+?但是,如果我有这样的课程,会发生什么.
using System;
using System.IO;
public class Class1
{
public class MappableObject
{
FileStream stream;
public int Blocks;
public int BlockSize;
public MappableObject(string FileName, int Blocks_in, int BlockSize_in)
{
Blocks = Blocks_in;
BlockSize = BlockSize_in;
// Just create the file here and set the size
stream = new FileStream(FileName); // Here we need more params of course to create a file.
stream.SetLength(sizeof(float) * Blocks * BlockSize);
}
public float[] GetBlock(int BlockNo)
{
long BlockPos = BlockNo * BlockSize;
stream.Position = BlockPos;
using (BinaryReader reader = new BinaryReader(stream))
{
float[] resData = new float[BlockSize];
for (int i = 0; i < BlockSize; i++)
{
// This line is stupid enough for accessing files a lot and the data is large
// Maybe someone has an idea to make this faster? I tried a lot and this is the simplest solution
// for illustration.
resData[i] = reader.ReadSingle();
}
}
retuen resData;
}
public void SetBlock(int BlockNo, float[] data)
{
long BlockPos = BlockNo * BlockSize;
stream.Position = BlockPos;
using (BinaryWriter reader = new BinaryWriter(stream))
{
for (int i = 0; i < BlockSize; i++)
{
// Also this line is stupid enough for accessing files a lot and the data is large
reader.Write(data[i];
}
}
retuen resData;
}
// For adding two MappableObjects
public static MappableObject operator +(MappableObject A, Mappableobject B)
{
// Of course we have to make sure that all dimensions are correct.
MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize);
for (int i = 0; i < Blocks; i++)
{
float[] dataA = A.GetBlock(i);
float[] dataB = B.GetBlock(i);
float[] C = new float[dataA.Length];
for (int j = 0; j < BlockSize; j++)
{
C[j] = A[j] + B[j];
}
result.SetBlock(i, C);
}
}
// For adding a single float to the whole data.
public static MappableObject operator +(MappableObject A, float B)
{
// Of course we have to make sure that all dimensions are correct.
MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize);
for (int i = 0; i < Blocks; i++)
{
float[] dataA = A.GetBlock(i);
float[] C = new float[dataA.Length];
for (int j = 0; j < BlockSize; j++)
{
C[j] = A[j] + B;
}
result.SetBlock(i, C);
}
}
// Of course this doesn't work, but maybe you can see the effect here.
// when the += is automimplemented from the definition above I have to create another large
// object which causes a loss of memory and also takes more time because of the operation -> altgough its
// simple in the example, but in reality it's much more complex.
public static MappableObject operator +=(MappableObject A, float B)
{
// Of course we have to make sure that all dimensions are correct.
MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize);
for (int i = 0; i < Blocks; i++)
{
float[] dataA = A.GetBlock(i);
for (int j = 0; j < BlockSize; j++)
{
A[j]+= + B;
}
result.SetBlock(i, A);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
你还说这+=是"自动实施"的好处.如果你尝试在C#中进行高性能计算,你需要有这样的功能来减少处理时间和内存消耗,如果有人有一个很好的解决方案,我很高兴,但不要告诉我,我必须用静态方法做到这一点,这只是一种解决方法,+=如果没有定义,我认为没有理由为什么C#执行实现,如果定义它将被使用.有人说没有区别+和+=防止错误,但这不是我自己的问题吗?