Proposed new architecture for defining the place of Eiffel classes in the .NET world

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