I'm sure there's a very simple answer to this but I can't figure it out. Why do operator overloads in C# have to be public?
-
-
The same reason for method overrides must have the same level of access modifier, I think.
Do you know of any operator that's not public?
-
Hi Bas,
When it comes to internal members, you must remember that internal members are accessible only within files in the same assembly. If you start thinking about this, you will see why operator overloads are public. Allow me to explain:
Imagine you were to create a class library called PointLibrary.dll that contains a Point class in the PointLibrary namespace that looks like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace PointLibrary { public class Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) { X = x; Y = y; } // Operator overload for +, it must be public and static public static Point operator +(Point p1, Point p2) { return new Point(p1.X + p2.X, p1.Y + p2.Y); } // Operator overload for -, it must be public and static public static Point operator -(Point p1, Point p2) { return new Point(p1.X + p2.X, p1.Y + p2.Y); } public override string ToString() { return string.Format("[X:{0}, Y:{1}]", this.X, this.Y); } } }Now since the operator overload for + and - are public, I can use this library (PointLibrary.dll) and the Point type in my project by adding a reference to it and have the functionality of the operator overloads for + and -. This is great! Now If I could, and would have made the operator overloads internal, I would not have the ability to use + and - since I am on a different assembly than PointLibrary.dll, in this example, I am now in PointClient.exe:
using System; using System.Collections.Generic; using System.Linq; using System.Text; // The PointLibrary namespace is in another assembly // than PointClient.exe, if this Point class had any members with the internal // access modifier, I will not be able to see it here using PointLibrary; namespace PointClient { class Program { static void Main(string[] args) { Point point1 = new Point(10, 10); Point point2 = new Point(20, 20); Point point3 = point1 + point2; Console.WriteLine("point1 + point2 = {0}", point3); // I will see [X:30, Y:30] as an answer Console.ReadLine(); } } }Allowing operator overloads to be internal would be useless if you were to make to use of them and then distribute your class library to others who are coding in a different assembly and just making reference to your assembly for the Point class. I hope I was able to explain at least one reason why operator overloads can't be internal.
-
9 hours ago, Bas wrote
I'm sure there's a very simple answer to this but I can't figure it out. Why do operator overloads in C# have to be public?
Why do you want them to not be public? I can't think of an example where an operator (e.g. +) is a good choice to overload on type "A", but that it's not a good idea to make that operator public.
The formal reason is it can give rise to ambiguity in the case where A : B and A privately overloads operator + and B publically overrides operator +. If two objects x and y of type A apply the + operator, which method is chosen becomes either
a) A compile time error, even though (B)x + (B)y is fine
b) Undecidable because A.opPlus(x, y) and B.opPlus(x, y) are valid or
c) Erratic, because either A.opPlus(x, y) is chosen when the operator is visible (e.g. private or internally) and B.opPlus(x, y) becomes chosen when the operator is publically visible.
d) Wrong because B.opPlus(x, y) is always chosen regardless of the fact that A.opPlus() exists.
This is all further confused by the fact that operators are static - not virtual methods, so
A ax = new A();
A ay = new A();
B bx = ax;
B by = ay;
ax + ay; // A.opPlus
ax + by; // B.opPlus
bx + ay; // B.opPlus
bx + by; // B.opPlusActually the C# grammar and the CLR both allow you to have non-public operators (they behave like case (c) above because that's how static method resolution works and operators are just static methods under the hood), but this is specifically not allowed by the compiler because it's confusing - particularly because methods are private by default, and the operator might be private simply because the developer forgot to write "public".
HTH
-
I don't get the "Why wouldn't you make it public?" arguments. There's a reason access modifiers exist, and it's good practice to give the bare required minimum of access to a type's members. If "why not" was a good reason, we could just make all methods public too and drop the internal modifier altogether.
A class's operator is no different than a class's method. I can (and should) make methods internal if I only need to use them from within the same assembly. Why can't I make an operator internal if I only need to use it from within the same assembly?
-
26 minutes ago, Bas wrote
I don't get the "Why wouldn't you make it public?" arguments. There's a reason access modifiers exist, and it's good practice to give the bare required minimum of access to a type's members. If "why not" was a good reason, we could just make all methods public too and drop the internal modifier altogether.
A class's operator is no different than a class's method. I can (and should) make methods internal if I only need to use them from within the same assembly. Why can't I make an operator internal if I only need to use it from within the same assembly?
For the reasons I've stated about leading to usage ambiguities - in your case it might be appropriate for you to have private or internal operators. In most cases it isn't - and it's more confusing for developers to be told that ".opAdd is inaccessible due to the protection level" on a source file that doesn't contain the string ".opAdd" or a definition of the type than it is for them to be told "operator overloads must be marked public" on the source file which contains the offending definition.
For your case your best bet is to define either internal static methods or instance methods, e.g.
class BasType
{
internal static BasType Add(BasType a, BasType b) {}
internal void AddInplace(BasType a) {}
}
Add your 2¢