Перегрузка оператора C# для`+='?
Я пытаюсь сделать перегрузку оператора +=
, но я не могу. Я могу только сделать перегрузку оператора +
.
как же так?
Edit
причина, по которой это не работает, заключается в том, что у меня есть векторный класс (с полем 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 станет совершенно новый вектор по ссылке, а также.
9 ответов:
Перегружаемые Операторы, от MSDN:
операторы присваивания не могут быть перегружены, но
+=
, например, оценивается с помощью+
, который может быть перегружен.более того, ни один из операторов присваивания не может быть перегружен. Я думаю, что это связано с тем, что будет эффект для сборки мусора и управления памятью, который является потенциальной дырой безопасности в CLR strong typed мир.
тем не менее, давайте посмотрим, что именно оператор. Согласно знаменитому книга Джеффри Рихтера, каждый язык программирования имеет свой собственный список операторов, которые компилируются в специальных вызовах метода, а сама среда 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
они равны! Так что
+=
оператор-это просто синтаксический сахар для вашей программы в C#, а можно просто перегрузить+
оператора.например:
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, вы действительно должны иметь векторы в качестве неизменяемого объекта. Результатом сложения двух векторов является a новая вектор, не первый, с различными размерами.
если по какой-то причине вам нужно изменить первый вектор, вы можете использовать эту перегрузку (но для меня это очень странное поведение):
public static Vector operator +(Vector left, Vector right) { left.x += right.x; left.y += right.y; return left; }
это происходит по той же причине, что оператор присваивания не может быть перегружен. Вы не можете написать код, который будет выполнять правильно задания.
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.
вы не можете перегрузка
+=
потому что это не совсем уникальный оператор, это просто синтаксический сахар.x += y
- это просто сокращенный способ написанияx = x + y
. Потому что+=
определяется в терминах+
и=
операторы, позволяющие переопределять его отдельно, могут создавать проблемы в случаях, когдаx += y
иx = x + y
не вел себя точно так же.на более низком уровне, весьма вероятно, что компилятор C# компилирует оба выражения вплоть до того же байт-кода, что означает, что это очень вероятно время выполнения не могу относиться к ним по-разному во время выполнения программы.
я могу понять, что вы можете рассматривать его как отдельную операцию: в заявлении, как
x += 10
вы знаете, что вы можете мутироватьx
объект на месте и, возможно, сэкономить время/память, а не создавать новый объектx + 10
перед назначением его по старой ссылке.но подумайте код:
a = ... b = a; a += 10;
должны
a == b
в конце? Для большинства типов, нет,a
- это на 10 больше, чемb
. Но если бы вы могли перегрузить+=
оператор мутировать на месте, тогда да. Теперь подумайте, чтоa
иb
может быть передано в отдаленные части программы. Ваша возможная оптимизация может создать запутанные ошибки, если ваш объект начнет меняться там, где код этого не ожидает.другими словами, если производительность так важна, это не так слишком трудно заменить
x += 10
с вызовом метода, какx.increaseBy(10)
, и это намного яснее для всех участников.
Я думаю, что вы найдете эту ссылку информативной:Перегружаемые Операторы
операторы присваивания не может быть перегружен, но +=, например, есть оценка +, который может быть перегруженный.
Это из-за этого оператор не может быть перегружен:
операторы присваивания не может быть перегружен, но +=, например, есть оценка +, который может быть перегруженный.
просто перегрузка
+
оператора, из-за
x += y
равнаx = x + y
если вы перегружаете
+
оператор как это:class Foo { public static Foo operator + (Foo c1, int x) { // implementation } }
можно сделать
Foo foo = new Foo(); foo += 10;
или
foo = foo + 10;
это будет компилироваться и работать одинаково.
всегда один и тот же ответ на эту проблему: зачем вам нужен
+=
, Если вы получаете его бесплатно, если вы перегружаете+
. Но что произойдет, если у меня есть такой класс.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# не+=
имплементация, если она не определена, и если она определена, она будет использоваться. Некоторые люди говорят, что не имея разницы между+
и+=
предотвращает ошибки, но разве это мои проблемы?
У меня был точно такой же вопрос и я не могу ответить, это лучше, чем этот человек