Sobrecarga del operador de C# para'+='?


Estoy tratando de hacer sobrecargas de operador para +=, pero no puedo. Solo puedo hacer una sobrecarga de operador para +.

¿por qué?

Editar

La razón por la que esto no funciona es que tengo una clase Vectorial (con un campo X e Y). Considere el siguiente ejemplo.

vector1 += vector2;

Si mi sobrecarga de operador está establecida en:

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

Entonces el resultado no se agregará a vector1, sino que vector1 también se convertirá en un nuevo vector por referencia.

Author: vaxquis, 2011-07-05

9 answers

Operadores sobrecargables , de MSDN:

Los operadores de asignación no se pueden sobrecargar, pero +=, por ejemplo, se evalúa usando +, que se puede sobrecargar.

Aún más, ninguno de los operadores de asignación puede ser sobrecargado. Creo que esto se debe a que habrá un efecto para la recolección de basura y la gestión de memoria, que es un agujero de seguridad potencial en el mundo de CLR strong typed.

Sin embargo, vamos a ver qué es exactamente un operador ser. De acuerdo con el famoso libro de Jeffrey Richter, cada lenguaje de programación tiene su propia lista de operadores, que se compilan en llamadas a un método especial, y CLR en sí no sabe nada acerca de los operadores. Así que vamos a ver qué queda exactamente detrás de los operadores + y +=.

Vea este simple código:

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

Veamos el código IL para estas instrucciones:

  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

Ahora veamos este código:

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

Y el código IL para esto:

  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

Ellos son iguales! Así que el operador +=es solo azúcar sintáctica para su programa en C#, y simplemente puede sobrecargar el operador +.

Por ejemplo:

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

Este código será compilado y ejecutado con éxito como:

  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

Actualización:

De acuerdo con su actualización, como dice @EricLippert, realmente debería tener los vectores como un objeto inmutable. Resultado de la adición de los dos vectores es un nuevo vector, no el primero con diferentes tamaños.

Si, por alguna razón, necesitas cambiar el primer vector, puedes usar esta sobrecarga (pero en cuanto a mí, este es un comportamiento muy extraño):

public static Vector operator +(Vector left, Vector right)
{
    left.x += right.x;
    left.y += right.y;
    return left;
}
 130
Author: VMAtm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-30 12:03:22

Esto se debe a la misma razón por la que el operador de asignación no se puede sobrecargar. No puede escribir código que realice la tarea correctamente.

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

Los operadores de asignación no pueden ser sobrecargado, pero +=, por ejemplo, es evaluado usando +, que puede ser sobrecargar.

De MSDN.

 16
Author: agent-j,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-05 18:31:31

No puedes sobrecargar += porque no es realmente un operador único, es solo azúcar sintáctico. x += y es solo una forma abreviada de escribir x = x + y. Debido a que += se define en términos de los operadores + y =, permitir que lo reemplace por separado podría crear problemas, en los casos en que x += y y x = x + y no se comporten exactamente de la misma manera.

En un nivel inferior, es muy probable que el compilador de C# compile ambas expresiones en el mismo bytecode, lo que significa que es muy probable que el runtime no pueda tratarlos de manera diferente durante la ejecución del programa.

Puedo entender que tal vez quieras tratarlo como una operación separada: en una instrucción como x += 10 sabes que puedes mutar el objeto x en su lugar y quizás ahorrar algo de tiempo/memoria, en lugar de crear un nuevo objeto x + 10 antes de asignarlo sobre la referencia anterior.

Pero considere este código:

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

Debería a == b al final? Para la mayoría de los tipos, no, a es 10 más que b. Pero si pudiera sobrecargar el operador += para mutar en su lugar, entonces sí. Ahora considere que a y b podrían pasarse a partes distantes del programa. Su posible optimización podría crear errores confusos si su objeto comienza a cambiar donde el código no espera que lo haga.

En otras palabras, si el rendimiento es tan importante, no es demasiado difícil reemplazar x += 10 con una llamada a un método como x.increaseBy(10), y es mucho más claro para todos involucrados.

 16
Author: benzado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-06 04:27:07

Creo que encontrará este enlace informativo: Operadores sobrecargables

Los operadores de asignación no pueden ser sobrecargado, pero +=, por ejemplo, es evaluado usando +, que puede ser sobrecargar.

 16
Author: pickypg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-07 23:52:54

Esto se debe a que este operador no se puede sobrecargar:

Los operadores de asignación no pueden ser sobrecargado, pero +=, por ejemplo, es evaluado usando +, que puede ser sobrecargar.

MSDN

Solo sobrecarga + operador, debido a

x += y igual a x = x + y

 9
Author: Andrew Orsich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-05 21:06:54

La sobrecarga del operador para + se usa en el operador +=, A += B es igual a A = operator+(A, B).

 6
Author: Alex Sedow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-06 16:28:56

Si se sobrecarga + operador como este:

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

Puedes hacer

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

O

 foo = foo + 10;

Esto se compilará y ejecutará igualmente.

 6
Author: Bala R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-06 19:37:24

Siempre hay la misma respuesta a este problema: ¿Por qué necesita el +=, si lo obtiene de forma gratuita si sobrecarga el +. Pero qué pasa si tengo una clase como esta.

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

¿Todavía dices que es bueno que el += es "auto-implementado". Si intenta hacer computación de alto rendimiento en C# necesita tener tales características para reducir el tiempo de procesamiento y el consumo de memoria, si alguien tiene una buena solución es muy apreciado, pero no me diga que tengo que hacer esto con métodos estáticos, esto es solo una solución y no veo ninguna razón por la que C# haga la implementación += si no está definida, y si está definida se usará. Algunas personas dicen que no tener una diferencia entre + y += previene errores, pero ¿no es este mi propio problema?

 6
Author: msedi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-17 12:06:48

Tuve exactamente la misma pregunta y no puedo responderla mejor que esta persona tiene

 3
Author: Community,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:26