Почему С# не поддерживает перегрузку операторов с передачей по ссылке?

Является ли это ограничением CLR или решением языкового дизайна? Я попытался сделать это в C++/CLI, конечно, там, где это работает, потому что нужно поддерживать собственный C++:

public ref class Test
    {
        public:
        static Test^ operator &( Test^ msg, int& i )
        {
            i = i + 1;

            return nullptr;
        } 
    };

а затем посмотрел на пропущенный компилятором вывод:

public: static Test __gc* op_BitwiseAnd(Test __gc* msg, Int32 __gc** modopt(IsImplicitlyDereferenced __gc*) i)
{
    i[0] += 1;
    return 0;
}

Я пошел дальше и попытался вызвать этот оператор из проекта С# - и, конечно, мне нужно было [небезопасно] сделать это (мне нужен был указатель):

Test t = new Test();
int i = 0;

unsafe
{
    t = t & &i;
} 

Очевидно, не так сложно реализовать для CLR? Мне очень не хватает передачи по ссылке при перегрузке операторов, и я хотел бы хотя бы осветить себя, почему этого нет?

Почему C# не может скрыть уродство за unsafe и указателями, когда нам нужно иметь дело со ссылочными переменными в наших перегрузках операторов? Даже если бы я выбрал этот уродливый обходной путь, он не сработал бы в Silverlight, где небезопасные операции запрещены...


person Ivan Zlatanov    schedule 31.10.2009    source источник


Ответы (2)


В C# ваши переменные никогда не изменяются вызываемыми объектами, если вы явно не передаете их в качестве ссылок (например, int.TryParse(s, out i), где вы явно указываете ключевое слово out). Эта функция усложнила бы задачу, позволив перегруженному оператору изменять содержимое операндов без вашего явного разрешения.

Например,

public static MyStruct operator + (ref MyStruct left, ref MyStruct right) {
    left = new MyStruct(); // !!!!!!!!
    return something(left, right);
}

Когда вы ссылаетесь на такой оператор в C#:

MyStruct x = new MyStruct();
MyStruct y = new MyStruct();
MyStruct z = x + y; // in C#, you never expect `x` to be changed.
person mmx    schedule 31.10.2009
comment
Нисколько. Просто сделать перегрузку оператора, чтобы получить параметр ref? - person Ivan Zlatanov; 31.10.2009
comment
Иван: тогда как бы вы назвали перегруженного оператора? Если метод принимает параметр ref, сайт вызова и сайт определения должен явно объявить параметр как ref. - person mmx; 31.10.2009
comment
Мердад: Это совершенно нормально. У них разные сигнатуры, поэтому вызов без ref никогда не будет соответствовать перегрузке ref. Что в этом такого сложного, что команда C# не реализовала это? Что, если мне действительно нужно изменить операнд, который не является ссылочным типом, как мне это сделать? - person Ivan Zlatanov; 31.10.2009
comment
Вы можете использовать z = x + (ref y); Я предполагаю. Но с какой целью? Операторы должны комбинировать значения, а не изменять их. - person Joren; 31.10.2009
comment
Иван: C# создан не для того, чтобы копировать сложности C++. Что, если мне действительно нужно изменить операнд, который не является ссылочным типом, как мне это сделать? Во-первых, если вам это действительно нужно, вы должны создать для этой цели метод, а не оператор. Вы злоупотребляете перегрузкой оператора. Во-вторых, что, если вызываемый объект не осознает вашего решения и вдруг увидит, что его переменная изменена в выражении? Это вызовет головную боль при отладке. - person mmx; 31.10.2009
comment
Что, если мне действительно нужно изменить операнд, который не является ссылочным типом, как мне это сделать? Вместо этого используйте метод. Я не думаю, что когда-либо возникала ситуация, когда вам действительно нужен оператор, особенно работающий таким образом. - person Joren; 31.10.2009
comment
Мой последний комментарий: s/callee/caller - person mmx; 31.10.2009
comment
@Ivan: Я думаю, вы упускаете из виду то, что пытаетесь сделать. C# имеет набор фундаментальных, основных правил, одно из которых заключается в том, что операторы никогда не будут изменять ссылки, над которыми они работают. На мой взгляд, это полезное и уместное правило, и поведение C++ может привести к ошибкам, которые очень трудно идентифицировать и отследить. Учитывая вашу реализацию C++/CLI, я был бы очень сбит с толку ее поведением, если бы я когда-либо сталкивался с ней, не зная реализации. - person jrista; 31.10.2009
comment
Джорен: Ты ошибаешься. Их много, и если вы посмотрите на библиотеку boost C++, вы найдете отличные методы, которые можно реализовать и на C#. Но я не хочу вдаваться в подробности, слишком много объяснять. Мне просто нужно было понять, почему эта перегрузка оператора не учитывается. - person Ivan Zlatanov; 31.10.2009
comment
Иван: Разработка языка подразумевает множество компромиссов. C# ставит простоту на первое место в списке целей разработки. Дело не в технической возможности. Конечно, это возможно. C++ является доказательством существования. С теми же рассуждениями вы можете возразить, почему в Java вообще нет перегрузки операторов. Поскольку разработчики думали о достижении целей разработки Java, преимущества не перевешивают затраты и повышенную сложность. - person mmx; 31.10.2009
comment
@Ivan: C# - это не C++, и на самом деле он специально направлен на то, чтобы закрыть некоторые лазейки в C++, которые могут привести к чрезмерно запутанному поведению и неожиданной модификации памяти. Я думаю, что это лучший пример того, ПОЧЕМУ C# не позволяет изменять операторы ref и подобные вещи. Не думайте, что C# == (C++ + дополнительно). C# наследует только структуру C++ и базовый синтаксис, а не все его поведение, и это правильно. - person jrista; 31.10.2009
comment
Разрешение типам переопределять такие вещи, как +=, с помощью метода, первым параметром которого является тип ref, дало бы семантику, полностью соответствующую ожиданиям, но позволило бы определенным типам реализовывать такие операторы способом, который был бы более эффективным или обеспечивал лучшую потокобезопасность, чем это было бы. быть возможным, так как оператор += обычно реализуется. - person supercat; 02.06.2014
comment
Я должен согласиться, что это может быть встроено предсказуемым образом и определенно принесет пользу. Яркий пример: перегрузка операторов для пользовательских типов матриц для использования в графическом приложении. Матрица 4x4 — это эффект разорвавшейся бомбы, которую нужно передавать по значению при каждом вызове, поэтому мы часто прибегаем к созданию вызовов функций, использующих функциональность передачи по ссылке, даже для таких методов, как сравнение на равенство, когда мы не собираемся разрешать ни то, ни другое. операнд, подлежащий изменению. Для этого было бы замечательно разрешить ту же семантику для перегрузки операторов. - person Mike Johnson; 25.08.2014
comment
C# действительно нуждается в форме const (также известной как неизменяемая переменная, хотя ее нельзя отбросить), тогда оператор может взять const ref и получить преимущество в производительности за счет передачи указателя, зная, что данные по-прежнему неизменяемы. - person David Jeske; 06.07.2016
comment
@jrista Кажется, что игнорируется тот факт, что операторы ++ и -- существуют в C #, могут быть перегружены и логически должны принимать ref неявно, как и в обычном языке. - person NetMage; 20.11.2020

Я думаю, это потому, что оператор (с более математической точки зрения, которую, кажется, принимает С#) логически является чем-то, что объединяет свои аргументы в новое значение, и никогда не предполагается, что оно что-то мутирует. С++, кажется, рассматривает операторы скорее как версию общих операций с более удобным синтаксисом, чем функции, чем как способ представления математики в частности. Я думаю, что в C# вы почти никогда не увидите таких вещей, как определение операторов для потоковых операций и тому подобное.

person Joren    schedule 31.10.2009
comment
В C# есть операторы ++ и --, которые делают всю эту аргументацию сомнительной. - person NetMage; 20.11.2020