我正在尝试为+=
执行运算符重载,但我无法做到。我只能为+
设置运算符重载。
怎么回事?
修改
这不起作用的原因是我有一个Vector类(带有X和Y字段)。请考虑以下示例。
vector1 += vector2;
如果我的操作员重载设置为:
public static Vector operator +(Vector left, Vector right)
{
return new Vector(right.x + left.x, right.y + left.y);
}
然后结果不会被添加到vector1,相反,vector1也将成为一个全新的Vector引用。
答案 0 :(得分:139)
分配运算符不能重载,但是
+=
例如使用+
进行评估,可以重载。
更重要的是,任何赋值运算符都不能超载。我认为这是因为垃圾收集和内存管理会产生影响,这是CLR强类型世界中潜在的安全漏洞。
然而,让我们看看运营商到底是什么。根据着名的Jeffrey Richter's book,每种编程语言都有自己的运算符列表,这些运算符列表是在特殊的方法调用中编译的,而CLR本身对运算符一无所知。因此,让我们看看+
和+=
运算符背后究竟是什么。
见这个简单的代码:
Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);
让我们查看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
现在让我们看看这段代码:
Decimal d1 = 10M;
d1 += 10M;
Console.WriteLine(d1);
和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
他们是平等的!因此,+=
运算符只是您的程序+
运算符。
例如:
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;
}
此代码将被编译并成功运行:
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
<强>更新强>
根据您的更新 - 正如@EricLippert所说,您确实应该将向量作为不可变对象。添加两个向量的结果是 new 向量,而不是第一个具有不同大小的向量。
如果由于某种原因你需要改变第一个向量,你可以使用这个重载(但对我来说,这是非常奇怪的行为):
public static Vector operator +(Vector left, Vector right)
{
left.x += right.x;
left.y += right.y;
return left;
}
答案 1 :(得分:17)
我认为您会发现此链接提供信息:Overloadable Operators
分配操作员不能 重载,但+ =,例如,是 使用+评估,可以是 超载。
答案 2 :(得分:16)
这是因为赋值运算符不能重载的原因相同。您无法编写能够正确执行赋值的代码。
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.
}
}
分配操作员不能 重载,但+ =,例如,是 使用+评估,可以是 超载。
来自MSDN。
答案 3 :(得分:16)
你不能重载+=
因为它不是一个独特的运算符,它只是syntactic sugar。 x += y
只是撰写x = x + y
的简便方式。由于+=
是根据+
和=
运算符定义的,因此在x += y
和x = x + y
没有的情况下,允许您单独覆盖它可能会产生问题行为方式完全相同。
在较低级别,C#编译器很可能将两个表达式编译为相同的字节码,这意味着运行时很可能在程序执行期间不同地处理它们。< / p>
我可以理解你可能希望将它视为一个单独的操作:在像x += 10
这样的语句中你知道你可以改变x
对象并节省一些时间/内存,而不是在通过旧引用分配它之前创建新对象x + 10
。
但请考虑以下代码:
a = ...
b = a;
a += 10;
最后应a == b
吗?对于大多数类型,不,a
比b
多10。但是,如果你可以重载+=
运算符以进行变异,那么是的。现在考虑a
和b
可以传递给程序的远端部分。如果您的对象开始在代码不期望的位置发生更改,那么您可能的优化可能会产生令人困惑的错误。
换句话说,如果性能非常重要,那么用x += 10
之类的方法调用替换x.increaseBy(10)
并不太难,并且对于所涉及的每个人来说都更加清晰。
答案 4 :(得分:9)
答案 5 :(得分:6)
+
运算符使用+=
的运算符重载,A += B
等于A = operator+(A, B)
。
答案 6 :(得分:6)
如果你像这样重载+
运算符:
class Foo
{
public static Foo operator + (Foo c1, int x)
{
// implementation
}
}
你可以做到
Foo foo = new Foo();
foo += 10;
或
foo = foo + 10;
这将编译并运行。
答案 7 :(得分:6)
这个问题总有相同的答案:如果你重载+=
,如果免费获得+
,为什么还需要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);
}
}
}
}
。但是,如果我有这样的课,会发生什么。
+=
你是否仍然说+=
是“自动实施”是好的。如果你尝试在C#中进行高性能计算,你需要有这样的功能来减少处理时间和内存消耗,如果有人有一个很好的解决方案,我们非常感激,但不要告诉我,我必须这样做使用静态方法,这只是一种解决方法,如果没有定义,我认为没有理由为什么C#执行+
实现,如果定义它将被使用。有人说+=
和{{1}}之间没有区别可以防止错误,但这不是我自己的问题吗?
答案 8 :(得分:3)
我有完全相同的问题,我不可能更好地回答this person has
答案 9 :(得分:0)
更好的设计方法是显式铸造。您绝对可以让Cast过载。