Перегрузка оператора С# для `+ =`?
Я пытаюсь выполнить операторские перегрузки для +=
, но я не могу. Я могу сделать только перегрузку оператора для +
.
Как получилось?
Edit
Причина, по которой это не работает, заключается в том, что у меня есть векторный класс (с полем X и Y). Рассмотрим следующий пример.
vector1 += vector2;
Если моя операционная перегрузка установлена на:
public static Vector operator +(Vector left, Vector right)
{
return new Vector(right.x + left.x, right.y + left.y);
}
Тогда результат не будет добавлен в вектор1, но вместо этого вектор1 станет также новым вектором по ссылке.
Ответы
Ответ 1
Перегружаемые операторы, из MSDN:
Операторы присваивания не могут быть перегружены, но, например, +=
оценивается с помощью +
, который может быть перегружен.
Более того, ни один из операторов присваивания не может быть перегружен. Я думаю, это связано с тем, что будет эффект для сбора мусора и управления памятью, который является потенциальным явлением безопасности в CLR в сильном типизированном мире.
Тем не менее, посмотрим, что такое оператор. Согласно знаменитой книге Джеффри Рихтера, каждый язык программирования имеет свой собственный список операторов, которые скомпилированы в специальных вызовах методов, а сама CLR не делает" ничего не знаю о операторах. Поэтому давайте посмотрим, что именно остается за операторами +
и +=
.
Смотрите этот простой код:
Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);
Посмотрите 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
Теперь посмотрим на этот код:
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
Update:
Согласно вашему обновлению, как говорит @EricLippert, вы действительно должны иметь векторы как неизменяемый объект. Результатом добавления двух векторов является новый вектор, а не первый с разными размерами.
Если по какой-то причине вам нужно изменить первый вектор, вы можете использовать эту перегрузку (но для меня это очень странное поведение):
public static Vector operator +(Vector left, Vector right)
{
left.x += right.x;
left.y += right.y;
return left;
}
Ответ 2
Я думаю, вы найдете эту ссылку информативной: Перегружаемые операторы
Операторы присваивания не могут быть перегружен, но + =, например, является оценивается с помощью +, что может быть перегружен.
Ответ 3
Это по той же причине, что оператор присваивания не может быть перегружен. Вы не можете написать код, который правильно выполнил бы назначение.
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.
Ответ 4
Вы не можете перегружать +=
, потому что это не уникальный оператор, это просто синтаксический сахар. x += y
- это всего лишь сокращенный способ написания x = x + y
. Поскольку +=
определяется в терминах операторов +
и =
, позволяя вам переопределить его отдельно, могут возникнуть проблемы, в случаях, когда x += y
и x = x + y
не ведут себя точно так же.
На более низком уровне очень вероятно, что компилятор С# скомпилирует оба выражения до одного и того же байт-кода, что означает, что, скорее всего, среда выполнения не может обрабатывать их по-разному во время выполнения программы.
Я могу понять, что вы можете рассматривать его как отдельную операцию: в выражении типа x += 10
вы знаете, что можете мутировать объект x
на месте и, возможно, сэкономить некоторое время/память, а не создавать новый объект x + 10
перед назначением его по старой ссылке.
Но рассмотрим этот код:
a = ...
b = a;
a += 10;
Должен ли a == b
в конце? Для большинства типов нет, a
на 10 больше, чем b
. Но если вы можете перегрузить оператор +=
, чтобы мутировать на месте, тогда да. Теперь рассмотрим, что a
и b
могут передаваться в отдаленные части программы. Ваша возможная оптимизация может создать запутывающие ошибки, если ваш объект начнет меняться, если код не ожидает его.
Другими словами, если производительность важна, не так сложно заменить x += 10
вызовом метода, как x.increaseBy(10)
, и это намного понятнее для всех участников.
Ответ 5
Это связано с тем, что этот оператор не может быть перегружен:
Операторы присваивания не могут быть перегружен, но + =, например, является оценивается с помощью +, что может быть перегружен.
MSDN
Просто перегрузите оператор +
, из-за
x += y
, равный x = x + y
Ответ 6
Перегрузка оператора для +
используется в операторе +=
, A += B
равна A = operator+(A, B)
.
Ответ 7
Если вы перегрузите оператор +
следующим образом:
class Foo
{
public static Foo operator + (Foo c1, int x)
{
// implementation
}
}
вы можете сделать
Foo foo = new Foo();
foo += 10;
или
foo = foo + 10;
Это будет компилироваться и работать одинаково.
Ответ 8
Всегда есть тот же ответ на эту проблему: зачем вам нужен +=
, если вы получите его бесплатно, если вы перегрузите +
. Но что произойдет, если у меня есть класс вроде этого.
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 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);
}
}
}
}
Вы все еще говорите, что хорошо, что +=
является "автоматически реализованным". Если вы попытаетесь выполнить высокопроизводительные вычисления на С#, вам нужно иметь такие функции, чтобы сократить время обработки и потребление памяти, если у кого-то есть хорошее решение, высоко оценили, , но не говорите мне, что я должен это делать с статическими методами это только обходное решение, и я не вижу причин, по которым С# реализует реализацию +=
, если она не определена, и если она определена, она будет использоваться. Некоторые люди говорят, что отсутствие разницы между +
и +=
предотвращает ошибки, но разве это не моя проблема?
Ответ 9
У меня был тот же самый вопрос, и я не могу ответить на него лучше, чем у этого человека есть
Ответ 10
Лучшим методом проектирования является Explicit Casting. Вы можете определенно перегрузить Casting.