As we've already mentioned, C# requires any method that is overridden to be
declared virtual
, and any method that overrides another to be declared
with the modifier override
.
We handle this by analyzing the class hierarchy and recording the analysis in the
digest XML file, which is available to the stylesheet that generates the C# code.
In addition, Java allows an overriding method to have a covariant return type:
if Expression.evaluate()
returns Sequence
, then Arithmetic.evaluate()
can return
AtomicValue
, given that AtomicValue
is a subclass of Sequence. C# doesn't allow
covariant return types until version 9.0 of the language, and we decided this was a new promised
feature that we would be unwise to rely on. Instead:
when we're analyzing the class hierarchy, we detect any use of covariance, and change the overriding method to use the same return type as its base method;
when we're analyzing the class hierarchy, we detect any use of covariance, and change the overriding method to use the same return type as its base method when we find a call to a method that's been overridden with a covariant return type, we insert a cast so the expected type remains as it was.
Java allows interfaces to define default implementations for methods; C# does not. The transpiler handles default method implementations by copying them into each subclass. This of course can lead to a lot of code duplication, so we have eliminated some of the cases where we were using default methods unnecessarily.