No doubt that the Conversation Operators makes developers life easier by bring in a kind of substitutability, but it comes with a price. If not properly designed/used, it could result in subtle errors that can be easily ignored.
Consider the following code.
public class Car { public string MakerName { get; set; } public Car(string Maker) => MakerName = Maker; } public class ElectricCar { public string BrandName { get; set; } public ElectricCar(string Brand) => BrandName = Brand; }
We have a class called Car, which exposes a single property – MakerName. We have another class ElectricCar, which exposes a property called BrandName. As seen, both classes are inherently incompatible, and you cannot implicitly/explicitly convert one to other. In order to solve this problem, we would be tempted to introduce an implicit conversion operator between the two classes.
We will modify ElectricCar as the following.
public class ElectricCar { public string BrandName { get; set; } public ElectricCar(string Brand) => BrandName = Brand; public static implicit operator Car(ElectricCar car) => new Car(car.BrandName); }
We have added an implicit operator that supports conversion between ElectricCar and Car Classes. This works fine and you can now convert between the two classes. Let’s now create a method which updates the BrandName for the class Car.
public static void UpdateMarkerName(Car car, string MakerName) => car.MakerName = MakerName;
By virtue of our implicit operator, we should be able to pass instances of both Car and Electric Car to this method. Let’s attempt to do so.
class Program { static void Main(string[] args) { var _car = new Car("Toyota"); var _electricCar = new ElectricCar("Toyota"); UpdateMarkerName(_car, "Tesla"); UpdateMarkerName(_electricCar, "Tesla"); Console.WriteLine($"Car : {_car.MakerName}"); Console.WriteLine($"Electric Car : {_electricCar.BrandName}"); Console.ReadLine(); } public static void UpdateMarkerName(Car car, string MakerName) => car.MakerName = MakerName; }
The output of above code would be curiously different, despite the implicit conversion.
Car : Tesla Electric Car : Toyota
Of course, it is not hard to understand why. The instance of ElectricCar, thanks to the implicit operator implementation, would be converted to an instance of Car when passed to the UpdateMarkerName method. However, the scope of converted object is within the UpdateMarkerName class. The Method, updates the MakerName, but eventually throws away the instance. It has no reflection on the original instance. This is in stark contrast to when an instance of Car is being passed.
This kind of error can be easily overlooked by the developer and can lead to issues that are hard to trace. This is one of the reasons why we need to keep away from the Conversion Operators.