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:
object + object
by default or myTpye + myType
without explicitly defining the +
operator beforehand.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?
Why is it called overloading?
It's called "overloading" because it is overloading. We "overload" a thing when we give two possible implementations for that thing and then must decide which to use (which is called overload resolution).
When we overload methods we give two or more implementations of a method with a given name. When we overload operators we give two or more possible implementations for an operator with a given syntax. It's the same thing.
Make sure you're not confusing overloading with overriding. Overloading is simply the existence of two methods/operators with the same name/syntax in the same declaration space. Overriding deals with how the content of a virtual method slot is filled in at runtime.
Do all objects by default have an implementation of all operators?
No.
Is
public static object operator +(object o1, object o2)
predefined somewhere and somehow?
No.
This would somehow imply that by default objects do not have predefined operators so then how come the term overloading?
I don't understand the question. Of course C# has predefined operators.
I don't want to expose this operator publicly
Then don't use an operator; make a private, internal or protected method.
The design of C# is that an operator is always part of the public surface area of a type. It is very confusing to have operators that have a meaning that depends on what accessibility domain the usage occurs in. C# has been carefully designed to be a "pit of quality" language, where the choices of the language designers lead you away from writing confusing, buggy, hard-to-refactor programs. Requiring that user-defined operators be public and static is one of those subtle design points.
(1) Objects by default do not have predefined operators
Sure they do; there are hundreds of predefined operators on a variety of objects. For addition, for example, there are the following predefined overloads of operator +
:
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)
Consult the C# specification for a list of all the predefined overloads of all the other operators.
Note that overload resolution for operators has two phases: first, overload resolution attempts to find a user-defined overload that is the unique best; only if doing so finds no applicable candidates are the predefined overloads considered by overload resolution.
(2) Defining operators is not described as creating an operator, it's described as overloading which somehow is inconsistent (to me) with point 1.
I don't understand why you find it inconsistent, or, for that matter, what you find inconsistent. The term "overload" is used consistently to describe both operators and methods; in both cases it means to use the same syntax to refer to two or more different things, and that ambiguity is then resolved via "overload resolution". The exact details of the method and operator overload resolution algorithms are different but they are similar in the overall algorithm: first a candidate set is identified, then inapplicable candidates are removed, then a betterness algorithm eliminates applicable candidates that are worse than another, then a bestness algorithm determines the unique best candidate that is left, if any.
(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.
I don't understand what point (3) has to do with points (1) or (2) at all. The restriction that operators must be part of the public surface area is to prevent the confusing situation of being able to add a Fruit
to an Animal
when you're inside class Apple
but not when you're inside class Giraffe
.
Operators are declared inside a class or struct and therefore "belong" to said type, they don't float "belonging" to no given type. So what am I overloading exactly when I declare an operator in a class?
You're overloading the operator.
That the same operator exists between ints does not mean I am overloading anything as that operator belongs to int. To me its the same as saying the
Foo.Hello()
andBar.Hello(string hello)
are overloads ofHello
. They are not as they are declared in two separate types. What is the difference with operators?
You've just accurately described the difference. Method overloading and operating overloading differ in many of their details.
If you want to take the position that Foo.Hello()
and Bar.Hello(string)
are "overloads" of Hello
, that's not a common position to take but it is logically consistent.
I was under the impression that you cannot change the access modifier when overloading.
Your impression is mistaken; you cannot change access modifiers when overriding a virtual method. You've confused that with overloading.
(And I note that there is one scenario in which you are required to change the access modifier when overriding a virtual method; can you deduce what it is?)
I was also under the impression that you can not declare an operator without at least one of the operands being of the type in which you declare the operator.
That's almost correct. A user-defined operator must have an operand of type T
, where T
is the enclosing class or struct type, or T?
if T
is a struct type.
So how can a third class have access to a given operator while another third class can't unless one belongs to an external assembly and the other doesn`t in which case I do not find it confusing at all and even useful?
You've mischaracterized my example, which could have been more clear. This is illegal:
public class Fruit
{
protected static Shape operator +(Fruit f, Animal a) { ... }
}
Because this is bizarre:
public class Apple : Fruit
{
...
Shape shape = this + giraffe; // Legal!
}
public class Giraffe : Animal
{
...
Shape shape = apple + this; // Illegal!
}
This is just one example. In general it is a strange thing to do, to make overload resolution of an operator depend on the accessibility domain, so the language designers ensured that this never happens by requiring user-defined operators to be public.
I simply find overloading confusing in the context of operators.
Many people do, including compiler writers. The user-defined operators portion of the specification is extremely difficult to parse, and the Microsoft implementation is a rich source of compiler bugs, many of which were my fault.
I don't see why simply declaring operators in a type has to described differently from how you would describe declaring any other static method.
Well, different things are different; operators are different than methods in many ways, including their overload resolution algorithms.
I have never particularly liked that C# has overloadable operators. The C# feature is a somewhat better designed version of the same feature in C++, but in both languages the feature in my opinion entails far larger costs than the corresponding user benefits.
Thank goodness at least C# does not thoroughly abuse the <<
operator the way idiomatic C++ does -- though of course it does abuse +
and -
for delegates.
object + object
because operators must be declared in the type of one of the operands; Point + Point
must be declared in Point
; you can't define object + object
because you are not declaring object
; note that all objects have (at the language level, at least) the ==
/ !=
operators==
/ !=
operators, or where operators are inherited from a non-trivial hierarchypublic
, so either make them public
, or use non-operator internal
methodsYou are probably confusing the term "overloading" with the term "overriding".
This is an example of overloading the method Foo
:
public class Parent
{
public sealed void Foo(int n) { }
}
public class Child : Parent
{
public sealed void Foo(string s) { }
}
This is an example of overriding the method Foo
:
public class Parent
{
public virtual int Foo() { return 0; }
}
public class Child : Parent
{
public override int Foo() { return 1; }
}
As you can see, overloading is adding a brand new signature that will always be called independently of the previous existing signature. Overriding involved providing a brand new implementation of an existing signature.
In this case you can think of the + operator as just another method (it essentially is, beneath some different syntax). If you were providing a new implementation of an existing signature, let's say the operator + (int a, int b)
signature, then you would be overriding that method. (Note that it's not possible to override an operator, or any static method, in C#.) What you're doing is overloading it by adding a new "signature" to the plus operator that takes a set of operands that didn't previously exist for any other overload of the plus operator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With