Conversions (explicit or implicit) and interfaces in C#
One of the most useful, although more potentially dangerous, features of C# is the possibility of overloading the conversion operators ( casting ) and specifically the implicit conversion.
Being able to overload the explicit conversion operator, although I understand it as a feature that adds orthogonality to the language, is not something I like. Before that, I prefer to create an AsXXX () method. In fact, it seems to me that a client of my class will find more logical an AsXXX () method than “a XXX casting” that seems to be more clear of what the intention is.
Implicit Conversion
But implicit casting is another story: it is especially useful when we have classes that are little more than a simple wrapper of another kind and we want to use it in the other type’s environment as easily as possible. Although it may seem like an aberration for a guy to convert implicitly (that is, without you doing anything), this is relatively common:
long l=10;
Ale, here you have an implicit conversion, where a value of type int, is automatically converted into a value of type long.
Well, overloading the implicit conversion operator has a limitation, and it only works with specific types, not with interfaces. The following does not work (I copy part of the class so you have the context):
class DynamicViewport { private readonly Func<IViewportFactory, IViewport> _vpFunc; private readonly IViewportFactory _viewportFactory; public DynamicViewport(IViewportFactory factory, Func<IViewportFactory, IViewport> viewportFunc) { _vpFunc = viewportFunc; _viewportFactory = factory; } public static implicit operator IViewport(DynamicViewport dp) { return dp._vpFunc(dp._viewportFactory); } }
Note that the code is a priori consistent: _vpFunc is a delegate that returns an IViewport so there is no problem out there.
Well, the reason why that does not work is that it is explicitly prohibited by the C# specification, but … why is it forbidden? Is there any reason?
Allowing overloading the casting operator is at least tricky since there are several cases that can easily confuse us. One is the loss of identity of objects. Yes, given the following code:
object obj = new MyClass(); var myObj = (MyClass)obj; var same = Object.ReferenceEquals(obj, myObj);
If I ask you for the value of “same” variable, I guess a large majority of you will tell me that same is true. The explicit casting operator in the language does not modify the objects, only the references. Now, imagine that the following operator was allowed:
public static explicit operator MyClass(object o) { return new MyClass(); }
Then, the same value of the previous code would be false. Thus, in order to avoid converting the development of C # into something similar to quantum physics, and that some conversions modified the identity and others did not, language developments decided to prohibit any explicit or implicit conversion whenever there is already one (and the conversion of base type to derived type already exists in C #).
Well, the same criterion applies to interfaces:
interface IMyClass { } class MyClass : IMyClass { } class OtherClass { } class Program { static void Main(string[] args) { var other = new OtherClass(); var iFirst = (IMyClass)other; } }
The following code compiles, since C # allows us to make the casting of any class to an interface and we know that this conversion has two possible results:
- Or else it fails in execution if OtherClass does not really implement IMyClass
- Either iFirst is an IMyClass but Object.ReferenceEquals (iFirst, other) is true. In other words, they are “the same” object.
If we could have a conversion (implicit or not) of our own, we could do that:
class OtherClass { public static explicit operator IMyClass (OtherClass oc) => new MyClass(); }
And then other and iFirst from the previous code would not have the same reference. So to avoid those situations, the language designers decided to cut it off.
Reading the last example, you ask yourself like noses C # allows it (that is, compiles). I copy it again:
interface IMyClass { } class MyClass : IMyClass { } class OtherClass { } class Program { static void Main(string[] args) { var other = new OtherClass(); var iFirst = (IMyClass)other; } }
I do not know about you, but I do not need a CLR that tells me that other does not implement the interface. That is to say:
- I have a variable other that is of type OtherClass
- The OtherClass class does not implement IMyClass
- Then it is from the box that the casting will fail.
We all agree, right? What did the language designers think when they decided to put up with these meaningless conversions?
Ah, shut up that they thought to support that:
interface IMyClass { } class MyClass : IMyClass { } class OtherClass { } class OtherClassChild : OtherClass, IMyClass {} class Program { static void Main(string[] args) { OtherClass other = new OtherClassChild(); var iFirst = (IMyClass)other; } }
As you can see when we have interfaces in the middle, everything gets complicated …
Summary
Well, in short, that there is no technical reason why the own conversions from / to interfaces are not allowed. The reason is because they open the door to certain scenarios that the language designers did not want to open and for that reason they prohibited them.
And, finally, to see that the conversions are not so simple, look at the following code:
class A { public int Foo { get; set; } } class B { public static implicit operator A(B b) => new B(); } class Program { static void Main(string[] args) { A a = new B(); var x = a.Foo; } }
What do you think this code does? Compile? Do not? And in case you compile … What happens? And because?
Regards!