Перегрузка оператора С# для `+ =`?

Я пытаюсь выполнить операторские перегрузки для +=, но я не могу. Я могу сделать только перегрузку оператора для +.

Как получилось?

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

Вы все еще говорите, что хорошо, что += является "автоматически реализованным". Если вы попытаетесь выполнить высокопроизводительные вычисления на С#, вам нужно иметь такие функции, чтобы сократить время обработки и потребление памяти, если у кого-то есть хорошее решение, высоко оценили, , но не говорите мне, что я должен это делать с статическими методами это только обходное решение, и я не вижу причин, по которым С# реализует реализацию +=, если она не определена, и если она определена, она будет использоваться. Некоторые люди говорят, что отсутствие разницы между + и += предотвращает ошибки, но разве это не моя проблема?

Ответ 10

Лучшим методом проектирования является Explicit Casting. Вы можете определенно перегрузить Casting.