que - Sobrecarga del operador C#para `+=`?
sobrecarga de operadores c# (9)
Estoy intentando hacer sobrecargas del operador para +=
, pero no puedo. Solo puedo hacer una sobrecarga de operador para +
.
¿Cómo?
Editar
La razón por la que esto no funciona es que tengo una clase Vector (con un campo X e Y). Considera el siguiente ejemplo.
vector1 += vector2;
Si la sobrecarga de mi operador está configurada para:
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á al vector1, sino que, en cambio, el vector1 también se convertirá en un vector nuevo por referencia.
Creo que encontrarás este enlace informativo: Operadores sobrecargados
Los operadores de asignación no pueden sobrecargarse, pero + =, por ejemplo, se evalúa usando +, que puede estar sobrecargado.
Esto se debe a la misma razón por la cual el operador de asignación no puede ser sobrecargado. No puede escribir código que realice la asignación 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 sobrecargarse, pero + =, por ejemplo, se evalúa usando +, que puede estar sobrecargado.
De MSDN .
Esto se debe a que este operador no puede estar sobrecargado:
Los operadores de asignación no pueden sobrecargarse, pero + =, por ejemplo, se evalúa usando +, que puede estar sobrecargado.
Solo sobrecargo +
operador, debido a
x += y
igual a x = x + y
La sobrecarga del operador para +
se usa en +=
operador, A += B
es igual a A = operator+(A, B)
.
No puede 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
. Como +=
se define en términos de los operadores +
y =
, permitirle anularlo por separado podría crear problemas, en los casos en que x += y
y x = x + y
no se comportaron 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 tiempo de ejecución no pueda tratarlas de forma diferente durante la ejecución del programa.
Puedo entender que es posible que desee tratarlo como una operación separada: en una declaración como x += 10
usted sabe que puede mutar el objeto x
en su lugar y tal vez 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 puede sobrecargar al 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 lo espera.
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 los involucrados.
Si sobrecarga al operador de esta manera:
class Foo
{
public static Foo operator + (Foo c1, int x)
{
// implementation
}
}
tu puedes hacer
Foo foo = new Foo();
foo += 10;
o
foo = foo + 10;
Esto se compilará y ejecutará por igual.
Siempre hay la misma respuesta a este problema: ¿por qué necesita el +=
, si lo obtiene de forma gratuita si sobrecarga el +
. Pero, ¿qué sucede 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);
}
}
}
}
¿Sigues diciendo que es bueno que +=
sea "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 apreciada, pero no me diga que debo hacer esto con métodos estáticos. , esto es solo una solución y no veo ninguna razón por la cual C # hace la implementación +=
si no está definida, y si está definida, se usará. Algunas personas dicen que no tener una diferencia entre +
y +=
evita los errores, pero ¿no es este mi problema?
Tenía la misma pregunta y no puedo responderla mejor que esta persona.
Operadores sobrecargados , de MSDN:
Los operadores de asignación no pueden sobrecargarse, pero
+=
, por ejemplo, se evalúa usando+
, que puede estar sobrecargado.
Aún más, ninguno de los operadores de asignación puede estar sobrecargado. Creo que esto se debe a que habrá un efecto para la recolección de basura y la administración de la memoria, que es un potencial agujero de seguridad en CLR.
Sin embargo, veamos qué es exactamente un operador. De acuerdo con el famoso libro de Jeffrey Richter , cada lenguaje de programación tiene su propia lista de operadores, que se compilan en un método especial de llamadas, y CLR en sí no sabe nada sobre los operadores. Entonces, veamos qué queda exactamente detrás de los operadores +
y +=
.
Vea este código simple:
Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);
Deje ver 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 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
¡Son iguales! Entonces el operador +=
es solo azúcar sintáctico para su programa en C # , y simplemente puede sobrecargar +
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 se compilará y ejecutará correctamente 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
Actualizar:
De acuerdo con su actualización, como dice @EricLippert, realmente debería tener los vectores como un objeto inmutable. El resultado de agregar los dos vectores es un nuevo vector, no el primero con diferentes tamaños.
Si, por algún motivo, necesitas cambiar el primer vector, puedes usar esta sobrecarga (pero en mi caso, 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;
}