Confused about operator overloading

Refresh

December 2018

Views

935 time

6

As I progress on my little math library I'm stumbling upon certain aspects of C# and the .NET Framework that I'm having some trouble understanding.

This time it's operator overloading and specifically the term overloading itself. Why is it called overloading? Do all objects by default have an implementation of all operators? That is, is:

public static object operator +(object o1, object o2)

predefined somewhere and somehow? If so, why then if I try o1 + o2 I get the compile time error Operator '+' cannot be applied to operands of type 'object' and 'object'? This would somehow imply that by default objects do not have predefined operators so then how come the term overloading?

I ask this, because in my math library, working with 3D geometry elements, I have the following structs: Vector and Point. Now, internally I want to allow the following construct to create vectors:

static Vector operator -(Point p1, Point p2) {...}

As this is not mathematically 100% correct but very useful internally to reduce code clutter, I don't want to expose this operator publicly, so my initial intention was to simply do:

internal static Vector operator -(Point p1, Point p2) {...}

Surprisingly (for me) I got the following compile time error: User-defined operator 'Geometry.operator +(Geometry.Vector, Geometry.Vector)' must be declared static and public".

Now this restriction that all operators must be public does seem to make some sense with the whole overloading aspect of operators but it seems that it is all kind of inconsistent:

  1. Objects by default do not have predefined operators: I can't do object + object by default or myTpye + myType without explicitly defining the + operator beforehand.
  2. Defining operators is not described as creating an operator, it's described as overloading which somehow is inconsistent (to me) with point 1.
  3. You cannot restrict access modifier of an operator, they have to be public, which does not make sense considering point 1. but sort of makes sense considering point 2.

Can somebody explain the mess I'm making of all this in simple terms?

3 answers

2
  1. вы не можете сделать , object + objectпотому что операторы должны быть объявлены в типе одного из операндов; Point + Pointдолжны быть объявлены вPoint ; вы не можете определить , object + objectпотому что вы не заявляющие object; обратите внимание , что все объекты имеют (на уровне языка, по крайней мере) ==/ !=операторы
  2. единственное важное различие в том , что вы не называть его переопределение - это не следует путать , что они каким - то образом полиморфный - они не являются; если вы хотите думать о нем , как «создание», это хорошо, но имейте в виду , что в нетривиальных случаях может быть несколько перекрывающихся операторов с участием тех же типов, например , с ==/ !=операторами, или где операторы унаследованных от нетривиальная иерархия
  3. что есть, то есть; спецификация говорит , что они должны быть public, так как делают их public, или использовать не операторные internalметоды
2

Вы, вероятно, путая термин «перегружать» с термином «переопределение».

Это пример перегрузки метода Foo:

public class Parent
{
    public sealed void Foo(int n) { }
}

public class Child : Parent
{
    public sealed void Foo(string s) { }
}

Это является примером переопределения метода Foo:

public class Parent
{
    public virtual int Foo() { return 0; }
}

public class Child : Parent
{
    public override int Foo() { return 1; }
}

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

В этом случае вы можете думать оператор + , как только другой метода (он по сути является, под каким - то другой синтаксисом). Если вы обеспечивали новую реализацию существующей подписи, скажем , на operator + (int a, int b)подпись, то вы бы отвергая этот метод. (Обратите внимание , что это не возможно , чтобы переопределить оператор, или любой статический метод, в C #.) То , что вы делаете, перегрузки, добавив новую «подпись» оператору плюс , который принимает множество операндов , которые ранее не существуют для любой другой перегрузки оператора плюс.

16

Почему это называется перегрузкой?

Это называется «перегрузка» , потому что это перегрузка. Мы «перегрузить» вещь , когда мы даем два возможных реализаций для этой вещи , а затем должны решить , какой из них использовать (который называется разрешение перегрузки ).

Когда мы перегружать методы мы даем два или более реализаций метода с заданным именем. Когда мы перегружать операторы мы даем два или более возможных реализаций для оператора с заданным синтаксисом. Это то же самое.

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

Есть все объекты по умолчанию имеет реализацию всех операторов?

Нет.

Является ли public static object operator +(object o1, object o2)предопределены где - то и как - то?

Нет.

Это как - то означает , что по умолчанию объекты не имеют предопределенные операторы так , то как же термин перегружать ?

Я не понимаю вопроса. Конечно C # имеет операторы предопределены.

Я не хочу подвергать этот оператор публично

Тогда не используйте оператор; сделать частный, внутренний или защищенный метод.

Конструкция C # является то, что оператор всегда является частью области общественной поверхности типа. Это очень запутанное, чтобы операторы, которые имеют значение, которое зависит от того, какого домена доступности происходит использование в. C # было тщательно разработано, чтобы быть «ямой качества» языка, где выборы языковых дизайнеров отведут вас от написания запутанного программы глючат, неподходящая для реорганизовать. Требование, чтобы операторы, определяемый пользователя быть открытыми и статическим является одним из тех тонких моментов дизайна.

(1) Объекты по умолчанию не имеют предопределенные операторы

Конечно , они делают; Существуют сотни предопределенных операторов на различных объектах. Для того, например, существуют следующие предопределенные перегрузки оператора +:

int + int
uint + uint
long + long
ulong + ulong
double + double
float + float
decimal + decimal
enum + underlying  (for any enum type)
underlying + enum
int? + int?
uint? + uint?
long? + long?
ulong? + ulong?
double? + double?
float? + float?
decimal? + decimal?
enum? + underlying?
underlying? + enum?
string + string
object + string
string + object
delegate + delegate (for any delegate type)

Обратитесь к # спецификации C для списка всех предопределенных перегрузок всех других операторов.

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

(2) Определение операторы не описываются, как создание оператора, это описывается как перегрузка, которая какой-то образом несовместимая (для меня) с точкой 1.

Я не понимаю, почему вы находите это непоследовательно, или, если на то пошло, что вы найдете противоречива. Термин «перегрузка» используется последовательно для описания обоих операторов и методов; в обоих случаях это означает использовать тот же синтаксис для обозначения двух или более различных вещей, и что двусмысленность затем решена с помощью «перегруженных». Точные детали алгоритмов методы и разрешения перегрузки оператора различны, но они похожи в общем алгоритме: первый набор кандидатов определен, то неподходящие кандидаты будут удалены, то алгоритм betterness устраняет применимые кандидат, которые хуже, чем другие, то bestness алгоритм определяет уникальный лучший кандидат, который остался, если таковые имеются.

(3) Вы не можете ограничить модификатор доступа оператора, они должны быть публичными, что не имеет смысла, учитывая точку 1. но вид имеет смысл, учитывая пункт 2.

Я не понимаю , что точка (3) имеет отношение к пунктам (1) или (2) на всех. Ограничение , что операторы должны быть частью области общественной поверхности, чтобы предотвратить запутанную ситуацию , чтобы быть в состоянии добавить Fruitк , Animalкогда вы находитесь внутри класса , Appleно не тогда , когда вы находитесь внутри класса Giraffe.

Операторы объявляются внутри класса или структуры и, следовательно, «принадлежит» указанного типа, они не плавают «принадлежащие» ни к данному типу. Так что я перегружать именно тогда, когда я объявляю оператор в классе?

Вы перегрузка оператора.

Это тот же оператор существует между Интс не значит , что я ничего перегружать как оператор принадлежит Int. Для меня его же , как говорят , Foo.Hello()и Bar.Hello(string hello)являются перегрузками Hello. Они не являются , поскольку они объявлены в двух отдельных типов. В чем разница с операторами?

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

Если вы хотите , чтобы занять позицию , что Foo.Hello()и Bar.Hello(string)в «перегруженных» из Hello, что это не общая позиция , чтобы взять , но это логически последовательным.

Я был под впечатлением, что вы не можете изменить модификатор доступа при перегрузке.

Ваше впечатление ошибочно; Вы не можете изменить модификаторы доступа при переопределении виртуального метода . Вы путать это с перегрузкой .

(И я отмечаю , что есть один сценарий , в котором вы обязаны изменить модификатор доступа при переопределении виртуального метода, вы можете вывести , что это такое?)

Я также был под впечатлением, что вы не можете объявить оператор без по крайней мере один из операндов такого типа, в котором вы объявляете оператор.

Это почти правильно. Определяемый пользователем оператор должен иметь операнд типа T, где Tэто вшита класс или структуру типа, или T? , если Tэто тип структуры.

Так как же третий класс иметь доступ к данному оператору, а другой третий класс не могу, если один не принадлежит к внешней сборке и другим Безразлично `в этом случае я не считаю, что сбивает с толком вообще и даже полезно?

Вы неправильно охарактеризовал мой пример, который мог бы быть более ясным. Это незаконно:

public class Fruit 
{ 
    protected static Shape operator +(Fruit f, Animal a) { ... } 
}

Потому что это странно:

public class Apple : Fruit
{ 
    ...
    Shape shape = this + giraffe; // Legal!
}
public class Giraffe : Animal
{
    ...
    Shape shape = apple + this; // Illegal!
}

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

Я просто найти перегрузки путаницы в контексте операторов.

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

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

Ну, разные вещи разные; операторы отличаются от методов во многих отношениях, в том числе их алгоритмов разрешения перегрузки.

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

Слава Богу , по крайней мере , C # не полностью засоряйте <<оператору путь идиоматических C ++ делает - хотя, конечно , это не злоупотребление +и -для делегатов.