Class ANY is the same in classic Eiffel and Eiffel for .NET. It does not inherit from anything. Instead, the class SYSTEM_OBJECT, is, like any other class, a descendant of ANY. SYSTEM_OBJECT has the following role:
Class ANY appears as:
class ANY convert to_dotnet: {SYSTEM_OBJECT} ... end
In other words it has one new feature, a conversion function to_dotnet: {SYSTEM_OBJECT}
which is also present in classic Eiffel but with a precondition restricting its
use to .NET mode.
There is one magic rule: any descendant of ANY
that is not a descendant of SYSTEM_OBJECT also
automatically admits to_dotnet as conversion
procedure, as if it had the same convert clause as ANY above. This means that
one can pass any Eiffel object to a .NET routine accepting values of type
System.Object.
Where SYSTEM_OBJECT is defined as follows, providing access to the universal features of .NET (`get_type' etc.):
class SYSTEM_OBJECT inherit ANY redefine all end feature -- Comparison equals (other: SYSTEM_OBJECT): BOOLEAN equals (a, b: SYSTEM_OBJECT): BOOLEAN reference_equals (other: SYSTEM_OBJECT): BOOLEAN feature -- Access get_hash_code: INTEGER get_type: TYPE feature -- Output to_string: SYSTEM_STRING feature {NONE} finalize frozen memberwise_clone: SYSTEM_OBJECT end
where `redefine all end' means that all routines inherited from
ANY are re-implemented specifically for .NET
external classes.
As a result of these rules, all classes are descendants of
ANY, following with the usual Eiffel rule. This
means that one can use ordinary Eiffel container classes, in particular those of
EiffelBase and EiffelVision, to store and retrieve any .NET object, whether an
instance of an Eiffel class or of a non-Eiffel class, without any particular
action on the programmer's side.
The other way round, as noted, Eiffel objects can be passed directly as
arguments to .NET routines.
Breaking change: to call a .NET (non-Eiffel) universal feature -- i.e. a feature
of SYSTEM_OBJECT, such as get_type -- on an Eiffel
object, you may not any more use e.g. a.get_type, but must write
a.to_dotnet.get_type
This is probably a good thing for clarity. We expect the call to to_dotnet
will be free performance-wise.
Now the big question is how to perform the dynamic binding.
Given
a, b: ANY a.is_equal (b)
we need to ensure that this code works even when `a' is attached to .NET type. I believe that we can achieve that by checking which routine Id is called and if it is one of the predefined one from ANY then we generate a special code that does a branching such as:
intermediate: SYSTEM_OBJECT intermediate ?= a if intermediate /= Void then feature {ISE_RUNTIME}.is_equal (a, b) else a.is_equal (b) end