Подтвердить что ты не робот

Почему '=' не перегружается в С#?

Мне было интересно, почему я не могу перегрузить '=' в С#? Могу ли я получить более подробное объяснение?

4b9b3361

Ответ 1

Языки, управляемые памятью, обычно работают со ссылками, а не с объектами. Когда вы определяете класс и его члены, вы определяете поведение объекта, но когда вы создаете переменную, вы работаете со ссылками на эти объекты.

Теперь оператор = применяется к ссылкам, а не к объектам. Когда вы назначаете ссылку на другую, вы фактически делаете опорную точку приема на тот же объект, что и другая ссылка.

Type var1 = new Type();
Type var2 = new Type();

var2 = var1;

В приведенном выше коде в куче создаются два объекта, один из которых относится к var1, а другой - к var2. Теперь последний оператор делает опорную точку var2 тем же самым объектом, что и var1. После этой строки сборщик мусора может освободить второй объект, и в памяти есть только один объект. В целом процесс не применяется к самим объектам.

Возвращаясь к тому, почему = нельзя перегрузить, реализация системы - единственная разумная вещь, которую вы можете сделать со ссылками. Вы можете перегружать операции, которые применяются к объектам, но не к ссылкам.

Ответ 2

Если вы перегрузили '=', вы никогда не сможете изменить ссылку на объект после его создания. ... подумайте об этом - любой вызов для объекта ObjectWithOverloadedOperator = что-то внутри перегруженного оператора приведет к другому вызову перегруженного оператора... так что же может сделать перегруженный оператор? Может быть, установить некоторые другие свойства - или установить значение для нового объекта (неизменность)? Как правило, это не означает, что "=".

Однако вы можете переопределить неявные и явные операторы литья: http://www.blackwasp.co.uk/CSharpConversionOverload.aspx

Ответ 3

Потому что на самом деле это не имеет смысла.

В С# = присваивается ссылка объекта на переменную. Таким образом, он работает с переменными и объектными ссылками, а не с объектами. Нет смысла перегружать его в зависимости от типа объекта.

В С++ определяющий оператор = имеет смысл для классов, экземпляры которых могут быть созданы, например. на стек, потому что сами объекты хранятся в переменных, а не ссылки на них. Поэтому имеет смысл определить, как выполнить такое присвоение. Но даже в С++, если у вас есть набор полиморфных классов, которые обычно используются с помощью указателей или ссылок, вы обычно явно запрещаете их копировать, объявляя operator = и конструктор копирования как private (или наследуя от boost:: noncopyable), из-за точно так же, как и почему вы не переопределяете = в С#. Просто, если у вас есть ссылка или указатель класса A, вы действительно не знаете, указывает ли он на экземпляр класса A или класса B, который является подклассом A. Так вы действительно знаете, как выполнить = в этой ситуации?

Ответ 4

Собственно, перегрузка operator = имела бы смысл, если бы вы могли определить классы со значением семантики и выделить объекты этих классов в стеке. Но в С# вы не можете.

Ответ 5

Одно из возможных объяснений заключается в том, что вы не можете выполнять правильные обновления ссылок, если вы перегружаете оператор присваивания. Это буквально испортило бы семантику, потому что, когда люди ожидали ссылок на обновление, ваш оператор = также может делать что-то совсем другое. Не очень дружелюбный программист.

Вы можете использовать неявные и явные в/из операторов преобразования, чтобы смягчить некоторые из кажущихся недостатков невозможности перегрузить назначение.

Ответ 6

Я не думаю, что есть какая-то конкретная причина. Вообще, я думаю, идея такова:

  • Если ваш объект является большим, сложным объектом, выполнение чего-то, что не является назначением с помощью оператора =, вероятно, вводит в заблуждение.

  • Если ваш объект является небольшим объектом, вы можете также сделать его неизменным и возвращать новые копии при выполнении операций над ним, чтобы оператор присваивания работал так, как вы ожидаете из коробки (в качестве System.String делает.)

Ответ 7

Вы можете перегрузить назначение в С#. Просто не на целый объект, только на его членов. Вы объявляете свойство с установщиком:

class Complex
{
    public double Real
    {
        get { ... }
        set { /* do something with value */ }
    }

    // more members
}

Теперь, когда вы назначаете Real, выполняется ваш собственный код.

Назначение причины для объекта не заменяется потому, что оно уже определено языком, означающим что-то жизненно важное.

Ответ 8

Это разрешено в С++, и если не быть осторожным, это может привести к большому путанице и поиску ошибок.

В этой статье это объясняется очень подробно.

http://www.relisoft.com/book/lang/project/14value.html

Ответ 9

Потому что стрельба в ногу неодобрительно.

В более серьезной заметке можно только надеяться, что вы имели в виду сравнение, а не назначение. Эта структура содержит подробные положения о вмешательстве в оценку равенства/эквивалентности, поиск "сравнения" в справке или в Интернете с помощью msdn.

Ответ 10

Этот код работает для меня:

public class Class1
{

    ...

    public static implicit operator Class1(Class2 value)
    {
        Class1 result = new Class1();

        result.property = value.prop;

        return result;

    }

}

Ответ 11

Тип переопределения назначения

Есть два типа для переопределения назначения:

  1. Когда вы чувствуете, что пользователь может что-то упустить, и вы хотите, чтобы пользователь использовал "приведение", например, с плавающей точкой к целому, когда вы теряете плавающее значение

int a = (int)5.4f;

  1. Когда вы хотите, чтобы пользователь сделал это, даже не заметив, что он/она меняет тип объекта

float f = 5;

Как переопределить назначение

Для 1 используйте explicit ключевое слово:

public static explicit override ToType(FromType from){
    ToType to = new ToType();
    to.FillFrom(from);
    return to;
}

Для 2 используйте implicit ключевое слово:

public static implicit override ToType(FromType from){
    ToType to = new ToType();
    to.FillFrom(from);
    return to;
}

Обновить:

Обратите внимание: эта реализация может осуществляться в классе FromType или ToType, в зависимости от ваших потребностей, ограничений нет, один из ваших классов может содержать все преобразования, а другой не реализует код для этого.

Ответ 12

Возможность определить специальную семантику для операций присваивания будет полезна, но только если такая семантика может быть применена ко всем ситуациям, в которых одно место хранения данного типа было скопировано в другое. Хотя в стандартном С++ реализованы такие правила назначения, у него есть роскошь требовать определения всех типов во время компиляции. Все становится намного сложнее, если в список добавлены Reflection и generics.

В настоящее время правила в .net указывают, что для места хранения может быть установлено значение по умолчанию для его типа - независимо от того, что это такое - путем обнуления всех байтов. Далее они указывают, что любое место хранения может быть скопировано на другой однотипный тип, скопировав все байты. Эти правила применяются ко всем типам, включая дженерики. Учитывая две переменные типа KeyValuePair<t1,t2>, система может копировать один в другой без необходимости знать ничего, кроме требований к размеру и выравниванию этого типа. Если бы было возможно t1, t2 или тип любого поля в любом из этих типов, чтобы реализовать конструктор копирования, код, который скопировал один экземпляр структуры в другой, был бы намного сложнее.

Нельзя сказать, что такая способность дает некоторые существенные преимущества - возможно, что новая структура была разработана, преимущества операторов присваивания специальных значений и конструкторов по умолчанию будут превышать затраты. Однако затраты на внедрение будут существенными в новых рамках и, вероятно, непреодолимыми для существующего.