Create .NET Properties in Eiffel
.NET properties are a fundamental part of the CLR and CTS. They are used in a plethora of ways by Microsoft, ISVs and your other developers. For a number of users .NET properties are just a syntactical sugar unifying a getter function and setter routine, and opening up access to encapsulated internal fields. Other common uses of properties provide a means validate client input on a setter and raising an exception if the input does not conform, or supporting performance scenarios such as lazy evaluation and caching.
Properties open up the whole new world of development scenarios, from creating Windows Forms/WPF controls, web services, true reflection based applications and much, much more. EiffelEnvision is now written in Eiffel for .NET and makes heavy use of properties. EiffelEnvision's integration into MsBuild would be an impossibility without Eiffel for .NET properties.
Getting Started
The Eiffel language (what is refer to as "classic" Eiffel) has not been changed to support Eiffel for .NET. With the classic version of the language remaining the same, Eiffel Software had to provide extension points to support Eiffel for .NET, which is done through using the indexing
clauses (Soon to be note
due to ECMA conformance.) It is actually possible to write a .NET application using just Eiffel libraries and compile it upon a Linux installation, using classic code generation. The vice versa is also equally possible
Now lets get on with examples. Take the following snippet of Eiffel code:
deferred class TEXT_BASE feature -- Access text: SYSTEM_STRING -- Actual text representation invariant not_text_is_empty: not {SYSTEM_STRING}.is_null_or_empty (text) end
The examples introduces the class TEXT_BASE
, for all descendants whom wish to expose a representation in a textual form. In Eiffel clients of TEXT_BASE
have access to text
, by default, because text
is implicitly exported to ANY
.
When compiled for .NET the implementation class for TEXT_BASE
with contain a .NET field with the name "
Consumers of an assembly written in Eiffel for .NET have access to text
through a function call defined on TEXT_BASE
's generated interface. It is a function call because .NET interfaces prohibit field definitions for obvious reasons. A function call is not the most respected way to retrieve an object attribute as it implies some logical computation to arrive at the result and, in many cases, indicates that an object reference that is return will differ on subsequent calls. What is really needed is a property!
Through the use of indexing
and an Eiffel for .NET specific indexing term, property
, a property can be defined on a generated Eiffel class' interface.
text: SYSTEM_STRING -- Actual text representation indexing property: auto end
In C#, a consuming project would be able to use
Note that text
has been augmented with an indexing clauses and property term. The reserved property term value auto
is used to instruct the compiler to automatically generate a property name based on the Eiffel attribute name. Alternatively a different name can be supplied for the property for external consumers of the generated assembly. To accomplish this, instead of specify auto
specify an identifier name as an Eiffel string:
text: SYSTEM_STRING -- Actual text representation indexing property: "my_text_property" end
C# consumer clients will then have to use the
That's it, properties in Eiffel for .NET.
But Wait... There's More
So far you've only seen read-only property. Any attempt to set the property will result in a compilation error, because there is no associated property setter. Eiffel for .NET properties do not magical inherit new semantics to support setting without explicitly defining those semantics. To do so would break one of the core principles of Eiffel, allowing external the modification of a object's state.
The new assigner mechanism defined in the ECMA standard give way to the solution for creating a .NET property that is both read/write.
deferred class TEXT_BASE feature -- Access text: SYSTEM_STRING assign set_text -- Actual text representation indexing property: auto end feature -- Element change set_text (a_text: like text) -- Set `text' with `a_text' require not_a_text_is_empty: not {SYSTEM_STRING}.is_null_or_empty (a_text) do text := a_text ensure text_set: {SYSTEM_STRING}.equals (text, a_text) end invariant not_text_is_empty: not {SYSTEM_STRING}.is_null_or_empty (text) end
With the text
attribute mark with and assigner (using the assign
keyword), C# users will be able to do the following:
In addition, due to the declaration of an assigner, Eiffel clients of TEXT_BASE will also be able to use a similar code style convention:
feature -- Basic operations set_text (tb: TEXT_BASE; text: SYSTEM_STRING) require tb_attached: tb /= Void not_text_is_empty: not {SYSTEM_STRING}.is_null_or_empty (text) do -- Valid because the the Eiffel attribute `text' has the assigner `set_text'. tb.text := text ensure text_set: {SYSTEM_STRING}.equals (text, tb.text) end
In both cases for C# and Eiffel the resulting call to the above assignment is always a call to TEXT_BASE.set_text
. text
will never be set directly.
What Next?
In a move to make your Eiffel assemblies look and feel more like other assemblies created in other languages, I'll be publishing future articles that discuss "Single Types" and .NET constructors in Eiffel. Using a compound of these features allows consumers of Eiffel for .NET assemblies to use a more standard way to instantiating type and accessing members. I'll guide you through the simple steps it takes to convert code used with Eiffel assemblies:
...into...