Why Must C# Operator Overloads Be Static

Why must C# operator overloads be static?

Take a look at this post.

A couple of reasons, the primary seeming to be to preserve operator symmetry (such that the left hand side of a binary operation does not get special treatment, as being responsible for dispatching the operation).

Operator overloading - why static resolve?

See Eric Lipperts article Why are overloaded operators always static in C#?

Rather, the question we should be asking ourselves when faced with a potential language feature is "does the compelling benefit of the feature justify all the costs?" And costs are considerably more than just the mundane dollar costs of designing, developing, testing, documenting and maintaining a feature. There are more subtle costs, like, will this feature make it more difficult to change the type inferencing algorithm in the future? Does this lead us into a world where we will be unable to make changes without introducing backwards compatibility breaks? And so on.

In this specific case, the compelling benefit is small. If you want to have a virtual dispatched overloaded operator in C# you can build one out of static parts very easily. [...]

It is be possible to support instance based operators but the C# language designer did not see the big gain compared to the efforts needed to make it work correctly.

Why can't operators be overloaded inside a static class?

Operators must relate to instances of the type in which they are declared. Since you can't have instances of a static class, it makes no sense to define operators.

There are no "extension operators" in .NET.

For your purposes, consider implementing an IComparer<T> (covers < and >) and / or IEqualityComparer<T> (covers ==, or you might just use compare returning 0; it depends whether you consider "sorts equal" and "equal" as the same).

Why must overloaded operators be declared public?

To answer half part of your question you may see the blog post by Eric Lippert.

Why are overloaded operators always static in C#?

Rather, the question we should be asking ourselves when faced with a
potential language feature is "does the compelling benefit of the
feature justify all the costs?" And costs are considerably more than
just the mundane dollar costs of designing, developing, testing,
documenting and maintaining a feature. There are more subtle costs,
like, will this feature make it more difficult to change the type
inferencing algorithm in the future? Does this lead us into a world
where we will be unable to make changes without introducing backwards
compatibility breaks? And so on.

In this specific case, the compelling benefit is small. If you want to
have a virtual dispatched overloaded operator in C# you can build one
out of static parts very easily. For example:

public class B {
public static B operator+(B b1, B b2) { return b1.Add(b2); }
protected virtual B Add(B b2) { // ...

And there you have it. So, the benefits are small. But the costs are
large. C++-style instance operators are weird. For example, they break
symmetry. If you define an operator+ that takes a C and an int, then
c+2 is legal but 2+c is not, and that badly breaks our intuition about
how the addition operator should behave.

For your question's other part: Why they should be public.

I was not able to find any authentic source, but IMO, if they are not going to be public, and may be used inside a class only, then this can be done using a simple private method, rather than an overloaded operator.

You may see the blog post by RB Whitaker

Operator Overloading

All operator overloads must be public and static, which should make
sense, since we want to have access to the operator throughout the
program, and since it belongs to the class as a whole, rather than any
specific instance of the class.

C# Operator overloading ==

When you overload operator == you should do it in a static method that takes two instances as parameters:

public static bool operator == (MyClass leftSide, MyClass rightSide) {
return true;
}

public static bool operator != (MyClass leftSide, MyClass rightSide) {
return !(leftSide == rightSide);
}

static context makes the code for your operator feel more "symmetric", in the sense that the code performing the comparison does not belong to a left instance or to the right instance.

In addition, static makes it impossible to "virtualize" the operator (you could still do it inside the implementation by calling a virtual function, but then you have to do it explicitly).

Operator overloading on class that implements interface

Operator overloading exists in compile-time only, after compilation it is just a call to a specific static method with well-known name.

C# compiler recognizes operator overloading for Foo and replaces a == b with Foo.op_Equality(a,b). That means there is non-virtual static method op_Equality in class Foo after compilation. Therefore it is not possible to overload operators for interfaces - interfaces cannot have static methods. Moreover, operator methods are non virtual, that means actual method is resolved from types of variables, not types of values, so compiler will look for possible operator overloading based on variable types of a and b, if both of them are IFoo, then it can only look for op_Equality in IFoo type - which cannot have it.

You can provide custom logic only for methods which are resolved based on types of values - those are virtual methods, e.g. you can override Equals method for Foo type, but it won't help you with IFoo a == IFoo b, which will be resolved to Object.ReferenceEquals(a,b)

Confused about operator overloading

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() and Bar.Hello(string hello) are overloads of Hello. 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.



Related Topics



Leave a reply



Submit