`+ =`的C#运算符重载?

时间:2011-07-05 18:23:16

标签: c# .net c#-4.0 .net-4.0 operator-overloading

我正在尝试为+=执行运算符重载,但我无法做到。我只能为+设置运算符重载。

怎么回事?

修改

这不起作用的原因是我有一个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引用。

10 个答案:

答案 0 :(得分:139)

来自MSDN的

Overloadable Operators

  

分配运算符不能重载,但是+=例如使用+进行评估,可以重载。

更重要的是,任何赋值运算符都不能超载。我认为这是因为垃圾收集和内存管理会产生影响,这是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 sugarx += y只是撰写x = x + y的简便方式。由于+=是根据+=运算符定义的,因此在x += yx = x + y没有的情况下,允许您单独覆盖它可能会产生问题行为方式完全相同。

在较低级别,C#编译器很可能将两个表达式编译为相同的字节码,这意味着运行时很可能在程序执行期间不同地处理它们。< / p>

我可以理解你可能希望将它视为一个单独的操作:在像x += 10这样的语句中你知道你可以改变x对象并节省一些时间/内存,而不是在通过旧引用分配它之前创建新对象x + 10

但请考虑以下代码:

a = ...
b = a;
a += 10;

最后应a == b吗?对于大多数类型,不,ab多10。但是,如果你可以重载+=运算符以进行变异,那么是的。现在考虑ab可以传递给程序的远端部分。如果您的对象开始在代码不期望的位置发生更改,那么您可能的优化可能会产生令人困惑的错误。

换句话说,如果性能非常重要,那么用x += 10之类的方法调用替换x.increaseBy(10)并不太难,并且对于所涉及的每个人来说都更加清晰。

答案 4 :(得分:9)

这是因为这个操作符不能重载:

  

分配操作员不能   重载,但+ =,例如,是   使用+评估,可以是   超载。

MSDN

因为

而过载+运算符

x += y等于x = x + y

答案 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过载。