web-dev-qa-db-ger.com

Überladung des C # -Operators für `+ =`?

Ich versuche, Operatorüberladungen für += Durchzuführen, kann dies aber nicht. Ich kann nur eine Operatorüberladung für + Durchführen.

Woher?

Bearbeiten

Der Grund, warum dies nicht funktioniert, ist, dass ich eine Vektorklasse habe (mit einem X- und einem Y-Feld). Betrachten Sie das folgende Beispiel.

vector1 += vector2;

Wenn meine Bedienerüberlastung auf Folgendes eingestellt ist:

public static Vector operator +(Vector left, Vector right)
{
    return new Vector(right.x + left.x, right.y + left.y);
}

Dann wird das Ergebnis nicht zu vector1 hinzugefügt, sondern vector1 wird auch als Referenz zu einem brandneuen Vector.

Überladbare Operatoren von MSDN:

Zuweisungsoperatoren können nicht überladen werden, aber += Wird beispielsweise mit + Ausgewertet, das überladen werden kann.

Darüber hinaus kann keiner der Zuweisungsoperatoren überladen werden. Ich denke, das liegt daran, dass es Auswirkungen auf die Garbage Collection und die Speicherverwaltung geben wird. Dies ist eine potenzielle Sicherheitslücke in der CLR-Welt mit starkem Typ.

Lassen Sie uns trotzdem sehen, was genau ein Operator ist. Nach dem berühmten Jeffrey Richters Buch hat jede Programmiersprache eine eigene Operatorenliste, die in speziellen Methodenaufrufen zusammengestellt wird, und CLR selbst weiß nichts über Operatoren. Mal sehen, was genau hinter den Operatoren + Und += Bleibt.

Siehe diesen einfachen Code:

Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);

Lassen Sie sich den IL-Code für diese Anleitung anzeigen:

  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

Nun sehen wir uns diesen Code an:

Decimal d1 = 10M;
d1 += 10M;
Console.WriteLine(d1);

Und IL-Code dafür:

  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

Sie sind gleich! Der Operator += Ist also nur syntaktischer Zucker für Ihr Programm in C # , und Sie können den Operator + Einfach überladen.

Beispielsweise:

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

Dieser Code wird kompiliert und erfolgreich ausgeführt als:

  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

Update:

Laut Ihrem Update - wie der @EricLippert sagt, sollten Sie die Vektoren wirklich als unveränderliches Objekt haben. Das Ergebnis der Addition der beiden Vektoren ist ein neuer Vektor, nicht der erste mit unterschiedlichen Größen.

Wenn Sie aus irgendeinem Grund den ersten Vektor ändern müssen, können Sie diese Überladung verwenden (aber für mich ist dies ein sehr merkwürdiges Verhalten):

public static Vector operator +(Vector left, Vector right)
{
    left.x += right.x;
    left.y += right.y;
    return left;
}
141
VMAtm

Ich denke, Sie finden diesen Link informativ: Overloadable Operators

Zuweisungsoperatoren können nicht überladen werden, aber + = wird zum Beispiel mit + ausgewertet, das überladen werden kann.

17
pickypg

Dies liegt an demselben Grund, aus dem der Zuweisungsoperator nicht überladen werden kann. Sie können keinen Code schreiben, der die Zuweisung korrekt ausführen würde.

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.
   }
}

Zuweisungsoperatoren können nicht überladen werden, aber + = wird zum Beispiel mit + ausgewertet, das überladen werden kann.

From MSDN .

16
agent-j

Sie können _+=_ nicht überladen, da es sich nicht wirklich um einen eindeutigen Operator handelt, sondern nur um syntaktischer Zucker . _x += y_ ist nur eine Kurzform von _x = x + y_. Da _+=_ in Bezug auf die Operatoren _+_ und _=_ definiert ist und Sie sie separat überschreiben können, können Probleme auftreten, wenn _x += y_ und _x = x + y_ nicht zutreffen verhalten sich nicht genau so.

Auf einer niedrigeren Ebene ist es sehr wahrscheinlich, dass der C # -Compiler beide Ausdrücke bis auf den gleichen Bytecode kompiliert, was bedeutet, dass es sehr wahrscheinlich ist, dass die Laufzeit nicht kann Behandle sie während der Programmausführung anders.

Ich kann verstehen, dass Sie es wie eine separate Operation behandeln möchten: In einer Anweisung wie _x += 10_ wissen Sie, dass Sie das x -Objekt an der richtigen Stelle mutieren und möglicherweise etwas Zeit/Speicher sparen können, anstatt Erstellen Sie ein neues Objekt _x + 10_, bevor Sie es über die alte Referenz zuweisen.

Aber beachten Sie diesen Code:

_a = ...
b = a;
a += 10;
_

Soll _a == b_ am Ende stehen? Bei den meisten Typen ist a 10 mehr als b. Aber wenn Sie den Operator _+=_ überladen könnten, um an der richtigen Stelle zu mutieren, dann ja. Bedenken Sie nun, dass a und b an entfernte Teile des Programms weitergegeben werden könnten. Ihre mögliche Optimierung kann zu verwirrenden Fehlern führen, wenn sich Ihr Objekt dort ändert, wo der Code dies nicht erwartet.

Mit anderen Worten, wenn die Leistung so wichtig ist, ist es nicht allzu schwer, _x += 10_ durch einen Methodenaufruf wie x.increaseBy(10) zu ersetzen, und dies ist für alle Beteiligten viel klarer.

16
benzado

Dies liegt daran, dass dieser Operator nicht überladen werden kann:

Zuweisungsoperatoren können nicht überladen werden, aber + = wird zum Beispiel mit + ausgewertet, das überladen werden kann.

MSDN

Überladen Sie einfach + Operator, wegen

x += y gleicht x = x + y

9
Andrew Orsich

Die Operatorüberladung für + Wird im Operator += Verwendet, A += B Entspricht A = operator+(A, B).

6
Alex Sedow

Wenn Sie den Operator + Wie folgt überladen:

class Foo
{
    public static Foo operator + (Foo c1, int x)
    {
        // implementation
    }
}

du kannst tun

 Foo foo = new Foo();
 foo += 10;

oder

 foo = foo + 10;

Dies wird zu gleichen Teilen kompiliert und ausgeführt.

6
Bala R

Es gibt immer die gleiche Antwort auf dieses Problem: Warum brauchen Sie den +=, Wenn Sie ihn kostenlos erhalten, wenn Sie den + Überladen. Aber was passiert, wenn ich so eine Klasse habe?.

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

Sagen Sie immer noch, dass es gut ist, dass += "Automatisch implementiert" wird. Wenn Sie versuchen, Hochleistungsrechnen in C # zu betreiben, müssen Sie über solche Funktionen verfügen, um die Verarbeitungszeit und den Speicherverbrauch zu reduzieren. Wenn jemand eine gute Lösung hat, wird dies sehr geschätzt , aber sagen Sie es nicht Ich muss dies mit statischen Methoden tun, dies ist nur eine Problemumgehung und ich sehe keinen Grund, warum C # die += - Implementierung ausführt, wenn sie nicht definiert ist und wenn sie es ist definiert wird verwendet. Einige Leute sagen, dass ein Unterschied zwischen + Und += Fehler vermeidet, aber ist das nicht mein eigenes Problem?

6
msedi

Ich hatte genau die gleiche Frage und kann sie unmöglich besser beantworten als diese Person hat

3
user34537

Eine bessere Entwurfsmethode ist Explizites Casting. Sie können Casting definitiv überlasten.

0
N_E