mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 07:12:25 +01:00
Created Documentation branch for 25.02
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2485 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
[[Property:title|Constructors and Creation Procedures]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|f06eb414-c284-902b-0481-3f00da82d47e]]
|
||||
This section deals with what happens when objects, that is runtime instances of types, get created and initialized. When a new instance is created, there is an opportunity to initialize the state of the instance. This is done with a constructor in .NET, and with a creation procedure in Eiffel.
|
||||
|
||||
==Eiffel Creation Procedures==
|
||||
|
||||
Eiffel creation procedures are features of a class which can be used to initialize instances. Classes can have more than one creation procedure available. However, each creation procedure must ensure that the class invariant holds when the procedure completes execution. In other words, the creation procedure is there to initialize a newly created instance, and the class invariant guarantees that a newly initialized instance is actually valid.
|
||||
|
||||
There is nothing special about creation procedures themselves, they are just ordinary procedures (although by convention their names usually begin with the word "<code>make</code>"). What makes them creation procedures is the fact that their names are listed as creation procedures in the class text.
|
||||
|
||||
In Eiffel, a creation procedure can be applied to an instance at any time (not just at object creation). This is done sometimes to reinitialize existing instances.
|
||||
|
||||
==Constructors in .NET==
|
||||
|
||||
Like creation procedures in Eiffel, .NET constructors are used to initialize new instances of types. Constructors manifest themselves differently depending upon which .NET language you use. In C#, constructors always appear as a method having the same name as the class on which they are implemented. In Visual Basic .NET, they always appear as a Sub with the name <code>New</code>. Once compiled into an assembly, the metadata labels constructors as <code>. ctor</code>.
|
||||
|
||||
Constructors can have multiple versions by overloading. That is, each version would have a different set of argument types.
|
||||
|
||||
Constructors can only be applied when a new instance is created.
|
||||
|
||||
===Constructors as Eiffel Creation Procedures===
|
||||
|
||||
When types from .NET assemblies are made available to Eiffel systems, the constructors are presented as creation procedures. Just as constructors show up in the C# and VB.NET environments with names appropriate to those languages, so it is with Eiffel for .NET. Always, constructors will have feature names which begin with the word "<code>make</code>", the convention for creation procedure naming in Eiffel.
|
||||
|
||||
If there is only one version of the constructor, that version will be mapped to a single feature named <code>make</code>. However, if there are overloaded versions of the constructor, then these versions given names starting with "<code>make_from_</code>" and then followed with the argument names from the assembly metadata separated with the conjunction "<code>_and_</code>". Let's look at an example.
|
||||
|
||||
The .NET type <code>System.Drawing.Size</code>has an overloaded constructor with two versions. In the <code>System.Drawing</code> assembly metadata, these two constructor versions look like this:
|
||||
<code>
|
||||
void .ctor(int32 width, int32 height)
|
||||
void .ctor(System.Drawing.Point pt)
|
||||
</code>
|
||||
|
||||
So the argument names for the first version are <code>width</code> and <code>height</code>. For the second version there is only one argument named <code>pt</code>. The constructor versions as presented to Eiffel programmers as creation procedures look like this:
|
||||
<code>
|
||||
make_from_width_and_height (width: INTEGER; height: INTEGER)
|
||||
make_from_pt (pt: DRAWING_POINT)
|
||||
</code>
|
||||
|
||||
Presenting the names in this format handles the conflicts of overloading and provides reasonably intuitive names that comply with Eiffel naming conventions.
|
||||
|
||||
===Eiffel Creation Procedures as Constructors?===
|
||||
|
||||
Eiffel creation procedures do not map to constructors when Eiffel classes are compiled into assemblies. Rather, they are actually manifested as functions on a factory class in the namespace <code lang=text>Create</code> in the assembly. These functions return an initialized instance. In the section Type Organization there is more information about the organization of the types in assemblies built with Eiffel for .NET, along with an example of using types from such an assembly.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
[[Property:modification_date|Mon, 25 Sep 2023 09:37:39 GMT]]
|
||||
[[Property:publication_date|Mon, 25 Sep 2023 09:37:39 GMT]]
|
||||
[[Property:title|Eiffel Class and Feature Names]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|16e4a231-7aae-4b37-52fd-67876cc222ad]]
|
||||
Certain naming conventions are respected by Eiffel programmers. Although Eiffel is not case-sensitive, convention dictates the use of upper and lower case in particular situations. When you program in Eiffel for .NET, you create new Eiffel classes, but you also use types from .NET assemblies. These .NET types are presented to you in a view that is consistent with Eiffel conventions.
|
||||
|
||||
==Eiffel Class Names==
|
||||
|
||||
Convention dictates that Eiffel class names are always all upper case characters with words separated by the underscore ("_") character. Eiffel classes are not qualified by "namespace" as in some other languages. This means that Eiffel class names must be unique with in a system. It also means that any types from existing .NET assemblies will have their names mapped to conventional Eiffel class names in order to be used by Eiffel classes in EiffelEnvision
|
||||
|
||||
Here are some class names and their descriptions from the Eiffel Base Library. These class names comply with the Eiffel class naming convention.
|
||||
<code>
|
||||
STRING
|
||||
|
||||
Sequences of characters, accessible through integer indices in a contiguous range.
|
||||
|
||||
RANDOM
|
||||
|
||||
Pseudo-random number sequence, linear congruential method.
|
||||
|
||||
ARRAYED_LIST
|
||||
|
||||
Lists implemented by resizable arrays.
|
||||
|
||||
LINKED_QUEUE
|
||||
|
||||
Unbounded queues implemented as linked lists.
|
||||
|
||||
</code>
|
||||
|
||||
Now here are some type names from the .NET mscorlib as they appear as conventional Eiffel class names (i.e. , in the form in which they become available to Eiffel for .NET programmers), followed by their full .NET type and their Summary from the .NET library. names.
|
||||
<code>
|
||||
SYSTEM_STRING
|
||||
|
||||
System.String
|
||||
|
||||
Represents an immutable series of characters.
|
||||
|
||||
SYSTEM_RANDOM
|
||||
|
||||
System.Random
|
||||
|
||||
Represents a pseudo-random number generator, a device that produces a sequence of numbers that meet certain statistical requirements for randomness.
|
||||
|
||||
ARRAY_LIST
|
||||
|
||||
System.Collections.ArrayList
|
||||
|
||||
Implements the System.Collections.IListinterface using an array whose size is dynamically increased as required.
|
||||
|
||||
SYSTEM_QUEUE
|
||||
|
||||
System.Collections.Queue
|
||||
|
||||
Represents a first-in, first-out collection of objects.
|
||||
|
||||
</code>
|
||||
|
||||
In summary, Eiffel class names and type names from .NET assemblies made available to Eiffel programmers will be all upper case, with words separated by the underscore character.
|
||||
|
||||
===Eiffel Names for .NET Types===
|
||||
|
||||
How Eiffel compliant names are derived from .NET type names is fairly simple in most cases. The "simple" class name, that is, the word following the rightmost dot in the full class name, is converted to an Eiffel compliant name by making it upper case and separating in embedded words by underscore. In the example above, <code>System.Collection.ArrayList</code> becomes <code>ARRAY_LIST</code>.
|
||||
|
||||
The other cases in the example are not quite so simple. If the basic derivation produces a name which conflicts with a classname in the Eiffel Base Library, then it will be disambiguated. The simple derivation of <eiffel>System.String</eiffel> would be <eiffel>STRING</eiffel>, but this would conflict with Eiffel's <eiffel>STRING</eiffel> class, so <eiffel>System.String</eiffel> becomes available to Eiffel for .NET programmers as <eiffel>SYSTEM_STRING</eiffel>.
|
||||
|
||||
Sometimes it is better to disambiguate an entire assembly rather than handling individual exceptions to the simple derivation. This is done by specifying a common prefix for all types in the assembly. For example, EiffelEnvision uses a prefix of " <eiffel>DATA_</eiffel>" for all classes in the .NET assembly System.Data. As a result, the type <eiffel>System.Data.Constraint</eiffel> is available in Eiffel as class <eiffel>DATA_CONSTRAINT</eiffel>.
|
||||
|
||||
You'll see a little more about namespaces, assemblies, and Eiffel clusters in [[Type Organization|Type Organization]] .
|
||||
|
||||
===Similar Types from Both Libraries===
|
||||
|
||||
You may have noticed a similarity in the names and descriptions from the Eiffel Base Library and those from the .NET "mscorlib" library. This is not by accident. The Eiffel class <code>STRING</code> is a different class from the .NET type <code>System.String</code>, which Eiffel programmers see represented as Eiffel class <code>SYSTEM_STRING</code>. There is more on this subject in [[Similar Types Occurring in Both Libraries|Similar Types Occurring in Both Libraries]] .
|
||||
|
||||
==Eiffel Feature Names==
|
||||
|
||||
By convention, feature names in Eiffel use all lower case characters, and like class names, words are separated by underscore. Also as with class names, the names of members from .NET assemblies will be represented in a form that complies with the Eiffel convention.
|
||||
|
||||
Let's look at some simple examples. First some feature names from the Eiffel Base Library.
|
||||
<code>
|
||||
to_upper
|
||||
|
||||
From class STRING: Converts to upper case.
|
||||
|
||||
item_double
|
||||
|
||||
From class RANDOM: The current random number as a double between 0 and 1
|
||||
|
||||
</code>
|
||||
|
||||
Now check out these member names from the .NET "mscorlib" type library. These have been made available to Eiffel for .NET programmers in the Eiffel convention. Following the Eiffel name, you see their .NET member name and type name.
|
||||
<code>
|
||||
to_upper
|
||||
|
||||
Member ToUpper from typeSystem.String
|
||||
|
||||
Returns a new System.String with the same content as the target, except all upper case.
|
||||
|
||||
next_double
|
||||
|
||||
Member NextDouble from type System.Random
|
||||
|
||||
A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
|
||||
|
||||
</code>
|
||||
|
||||
So, Eiffel feature names, and the names of .NET members made available to Eiffel for .NET programmers, are all lower case with words separated by underscores.
|
||||
|
||||
==Overloaded .NET Member Names==
|
||||
|
||||
The .NET object model allows overloading of function names. This means that a .NET type can support multiple functions with the same name, that vary only by the types of the arguments they accept. For example, the .NET type System. Text. StringBuilder supports nineteen overloaded versions of the Append function. Here are a couple of examples, in their .NET forms:
|
||||
<code>
|
||||
.NET function signature: Append(System.String)
|
||||
|
||||
Member of type System.Text.StringBuilder
|
||||
|
||||
Appends a copy of the specified string to the end of this instance.
|
||||
|
||||
.NET function signature: Append(System.Char)
|
||||
|
||||
Member of type System.Text.StringBuilder
|
||||
|
||||
Appends the string representation of a specified Unicode character to the end of this instance.
|
||||
|
||||
</code>
|
||||
|
||||
The Eiffel programming language does not allow overloading routine names. That means that you cannot code multiple routines with the same name in a single class. That in itself is not a problem. But it also means that to work in the .NET environment, where overloading is allowed some compromise has to be made. So, what happens is this: if you are programming in Eiffel for .NET and you are using types from a .NET assembly, those types will be presented to you as if they are Eiffel classes. We have already seen that the type and feature names will be shown in the Eiffel naming convention. With overloaded feature names, the presentation will use name augmentation to disambiguate the overloaded versions. What you see is a distinct feature name for each overloaded version. The basic feature name is augmented by adding the types of its respective arguments, separated by underscore.
|
||||
|
||||
Let's look again at the two <code>Append</code> functions from <code>System.Text.StringBuilder</code>.
|
||||
<code>
|
||||
.NET function signature: Append(System.String)
|
||||
|
||||
Known to Eiffel as: append_string (value: SYSTEM_STRING)
|
||||
|
||||
Member of type System.Text.StringBuilder, known to Eiffel as STRING_BUILDER
|
||||
|
||||
Appends a copy of the specified string to the end of this instance.
|
||||
|
||||
.NET function signature: Append(System.Char)
|
||||
|
||||
Known to Eiffel as: append_character (value: CHARACTER)
|
||||
|
||||
Member of type System.Text.StringBuilder, known to Eiffel as STRING_BUILDER
|
||||
|
||||
Appends the string representation of a specified Unicode character to the end of this instance.
|
||||
|
||||
</code>
|
||||
|
||||
The overloading story does not end quite yet. The .NET object model allows the overloading of constructors. This issue will be discussed in the section Constructors and Creation Procedures.
|
||||
|
||||
==.NET Properties as Eiffel Features==
|
||||
|
||||
Properties in .NET provide:
|
||||
* the opportunity to '''strengthen encapsulation,''' because values cannot be receive assignment without executing the property's "set" code
|
||||
* '''uniform access ''' queries because properties are queries, but unlike previous C-style OO languages in which properties did not exist, if a property is used in programming a client class, the programmer does not need to know whether the data provided by the property was done so from memory or through computation. This leaves the producer of the class with the property the latitude to change the implementation without breaking existing clients.
|
||||
|
||||
In Eiffel, the same goals are fulfilled, but a little differently. Simple attributes are well-encapsulated, because the Eiffel programming language does not allow direct assignment to them from outside the control of their class. So any assignment of the form <code>x</code>. <code>f</code> := <code>y</code> is not valid in Eiffel. To allow client to set values of the attribute <code>f</code>, the producer of the class of which <code>x</code> is an instance would have built a command (a "<code>set_</code>" procedure) to do so. Then the code in a client to set <code>f</code> would look like this: <code>x</code>.<code>set_f</code> (<code>y</code>).
|
||||
|
||||
Uniform access is achieved in Eiffel through the way in which clientssee features which are queries. The code " <code>print</code> ( <code>x</code>.<code>count</code>)" applies the query <code>count</code> to the object attached to <code>x</code> and prints the result. You cannot tell by looking at this code whether <code>count</code> is an attribute or a function, that is, whether the <code>count</code> is returned from memory or through computation. In fact, it could be either, but that is a matter for its producer to deal with. As reuse consumers the implementation of <code>count</code>is not important to us ...but the fact that the producer can change the implementation without causing our code to need modification is very important to us.
|
||||
|
||||
Because Eiffel does not support properties directly, the propertiesof typeswhich Eiffel for .NET programmers usefrom .NET assemblies have to be mapped to Eiffel features.
|
||||
|
||||
In order to ask for the property's current value (technically, receiving the result of the property's <code>get</code> routine), a query feature is generated for the property. The query will be namedthe Eiffel name of the property.
|
||||
|
||||
As noted above, setting the value of a property cannot be done in Eiffel as it is done in C# and VB.NET because Eiffel disallows assignments of the form <code>x</code>.<code>f</code> := <code>y</code>. So, for each writable property, an Eiffel command feature is available to set the value of the property. The name for this command will be "<code>set_</code>" followed by the Eiffel name for the property.
|
||||
|
||||
As a result, the code for using a .NET property looks very much like the code to use an Eiffel attribute. In the following code fragment, an instance of the type <code>System.Windows.Forms.Form</code> which is available in Eiffel for .NET as <code>WINFORMS_FORM</code> is used by an Eiffel client. <code>System.Windows.Forms.Form</code> has a property <code>Text</code> which is of type <code>System.String</code>. Here the <code>Text</code> property is being set using the <code>set_text</code> feature, and then being recalled by using the query <code>text</code>.
|
||||
<code>
|
||||
local
|
||||
my_window: WINFORMS_FORM
|
||||
my_string: SYSTEM_STRING
|
||||
do
|
||||
create my_window.make
|
||||
my_window.set_text (my_window_title) -- Set Text property
|
||||
my_string := my_window.text -- Query Text property
|
||||
.
|
||||
.
|
||||
end
|
||||
</code>
|
||||
|
||||
==Static Features in .NET==
|
||||
|
||||
In the .NET object model it is possible for certain members of a type to be "static". When these members are used, they are used without an instance of the class as a target. In essence, they are called on the class itself.
|
||||
|
||||
In Eiffel for .NET, these staticmembers will made available with feature names derivedusing the same conventions as ordinary features, but applying them will be a bit different.
|
||||
|
||||
There is not a concept analogous to static members in Eiffel. The model for object-oriented computation in Eiffel specifies that whenever feature application takes place, there must be an object, i.e. an instance of some class, that serves as a target.
|
||||
|
||||
In order to use .NET types that include static members, a special syntax has been added into Eiffel for .NET. The following example uses this syntax to call a static function:
|
||||
<code>
|
||||
local
|
||||
my_window: WINFORMS_FORM
|
||||
do
|
||||
create my_window.make
|
||||
my_window.set_text (my_window_title)
|
||||
.
|
||||
.
|
||||
{WINFORMS_APPLICATION}.run_form (my_window)
|
||||
end
|
||||
|
||||
</code>
|
||||
|
||||
The type <code>System.Windows.Forms.Application</code> is used here. It is available to Eiffel under the name <code>WINFORMS_APPLICATION</code>. The static member being used is <code>Run</code>, in particular the overloaded version of <code>Run</code> which takes an argument of type <code>System.Windows.Forms.Form</code>. That version is available in Eiffel under the name <code>run_form</code>.
|
||||
|
||||
The important thing to see here is that when you need to apply a static member, you introduce the call with the keyword <code>feature</code>. Then enclose the type name in braces and apply the feature as if it were targeted to an object. This isfairly close to the way that the call would be made in C#, where the feature name would be applied to the type name, versus a target object:
|
||||
<code>
|
||||
{
|
||||
Form my_window;
|
||||
my_window = new Form();
|
||||
my_window.Text = "Hello World!";
|
||||
.
|
||||
.
|
||||
Application.Run(my_window); // This is C#
|
||||
}
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
[[Property:title|Eiffel for .NET Terminology]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|ab1ac480-e2d3-df5d-5f06-963899f1072d]]
|
||||
==Eiffel Terminology Defined for C# and VB.NET Programmers==
|
||||
|
||||
Eiffel programmers feel that it is important to have a set of precise terms with which to communicate about our method of software development. Like everything else in Eiffel, the use of certain terms is not accidental. They were chosen carefully to impart particular meaning. Many of the Terms that Eiffel programmers use are different from those used by developers using other object-oriented languages. But that does not mean that their meanings will be foreign to you. If you have some understanding of object-oriented technology, you will find that in many cases the Eiffel terms describe concepts with which you were already familiar, just under different names, and possibly with slightly different meanings.
|
||||
|
||||
The intention of this glossary is to give you a list of these terms with extended definitions that will relate the Eiffel terms to language with which you may be more familiar. Also, you will find an occasional term which comes from other programming cultures but is not used in Eiffel.
|
||||
{|
|
||||
|-
|
||||
| Term
|
||||
| Definition
|
||||
|-
|
||||
| Ancestors
|
||||
| The set of Classes from which a particular Class Inherits, including the Class itself. Somewhat analogous to Bases and Interfaces in C# and VB.NET (see Proper Ancestors).
|
||||
|-
|
||||
| Argument
|
||||
| An object which is passed to a routine. Called a Parameter in C# and VB.NET.
|
||||
|-
|
||||
| Assertion
|
||||
| A declaritive expression that evaluates to true or false. Assertions in Eiffel are of different types and are used primarily for expressing software specification to potential Reuse Consumers and ensuring that executing software complies with specification. Any violation of an assertion causes an exception, and is generally indicative of a defect in the software system.
|
||||
|-
|
||||
| Attribute
|
||||
| A Feature which represents storage used in every instance of a class. Used like a Field member in C#.
|
||||
|-
|
||||
| Class
|
||||
| Software text which is the static definition of a Type (or the pattern for a Type in the case of a Generic Class). Used in very nearly the same way as in C#. In C# Classes are used for reference types and Structures for value types. In Eiffel, Classes are used for both, but see Expanded Class.
|
||||
|-
|
||||
| Class invariant
|
||||
| An Assertion on a Class which defines the valid or stable state for objects which are instances of the Class. All instances of the Class must comply with the Class Invariant at any time that they are accessible to Clients.
|
||||
|-
|
||||
| Client
|
||||
| A Class whose role is that of Client in a Client/Supplier Relationship with another Class.
|
||||
|-
|
||||
| Client/Supplier relationship
|
||||
| One of the two Relationships that can exist between Classes. Client/Supplier is characterized by a situation in which one Class, called the Client, has or uses, instances of the other Class, called the Supplier. For example, if class "X" declares an entity "y" of type STRING, then Class X becomes a Client to Class STRING as a result. At the same time, Class STRING becomes a Supplier to class X.
|
||||
|-
|
||||
| Cluster
|
||||
| A group of classes (or recursively, clusters) which share some criteria which justifies their being grouped together. Classes are usually clustered together because they model similar entities or have similar functionality. For example, in the Eiffel Base Library, there is a cluster called "structures" which contains clusters for data structures. One cluster is "list" which contains classes like LINKED_LIST and TWO_WAY_LIST. Another "structures" cluster is "dispensers" which contains classes like "ARRAYED_STACK" and "PRIORITY_QUEUE". When .NET assemblies are made available to Eiffel programmers, each assembly is viewed as a cluster.
|
||||
|-
|
||||
| Command
|
||||
| A Class Feature which can change the state of the instance to which it is applied, and does not return a result. It is the Reuse Consumer's view of a Procedure.
|
||||
|-
|
||||
| Contract
|
||||
| Like a contract between human parties, a software Contract is a formal agreement between software components, Classes in the case of Eiffel. The contract is written in the Assertions of a Supplier Class and specifies in precise terms those conditions with which potential Clients may interact with the Supplier. The contract for a particular Class is composed of the contracts for each Exported Routine of the Class as well as the Class Invariant.
|
||||
|-
|
||||
| Creation Procedure
|
||||
| A Procedure which can be used to initialize and instance of a Class. Similar to a Constructor in C# or VB.NET, but slightly different. A Creation Procedure can be applied when an object is being created, but can also be applied later to reinitialize an existing object (such is not the case with Constructors). Also, Creation Procedures are responsible for ensuring that newly created objects comply with their Class Invariants.
|
||||
|-
|
||||
| Deferred Class
|
||||
| A class which has at least one Deferred Feature. The term "Deferred" is used in the Eiffel culture like the term "Abstract" in .NET. Like an abstract class in C# or VB.NET, it is not possible to make direct runtime instances of a Deferred Class.
|
||||
|-
|
||||
| Deferred feature
|
||||
| A Class Feature which has not implementation. Like a virtual method in C#. Can apply to Commands or Queries.
|
||||
|-
|
||||
| Descendents
|
||||
| The set of all Classes which Inherit from a particular Class, including the Class itself. Somewhat analogous to subclasses in C# and VB.NET (see Proper Descendants).
|
||||
|-
|
||||
| Design by contract
|
||||
| A method of developing software in which a System is composed of a set of interacting Classes which cooperate based upon precisely defined Contracts.
|
||||
|-
|
||||
| Direct instance
|
||||
| A runtime Object of some type, based directly on the definition from a specific Effective Class. See Instance for more information. Usually used in conjunction with the class name, e.g. "an instance of class POLYGON".
|
||||
|-
|
||||
| Effective class
|
||||
| A Class which has no Deferred Features. Only if a Class is Effective can it have direct runtime instances. Effective classes are called Concrete Classes in some programming cultures.
|
||||
|-
|
||||
| Eiffel Metadata Consumer
|
||||
| Applicable to Eiffel for .NET only, this is a mechanism that works behind the scenes to make Types from .NET assemblies available to Eiffel programmers. The Metadata Consumer reconciles the differences between the naming conventions of Eiffel and .NET by providing an Eiffel compliant view of the Types in .NET assemblies.
|
||||
|-
|
||||
| Expanded Class
|
||||
| Like a value type in C#, but with some restrictions. .NET value types are viewed to Eiffel programmers as Expanded Classes. For reference types, an object field holds a reference to a runtime object (or is Void). For value types, the object field holds the fields of the runtime object. A Type based on an Expanded Class is an Expanded Type.
|
||||
|-
|
||||
| Exported feature
|
||||
| A Feature which is available to Client Classes. Analogous to "public" in C# and VB.NET. However, Exporting in Eiffel has fine granularity. It is possible to make certain Features available to Clients of all types or only to Clients which conform to certain named Classes (vs Public, Protected, Private)
|
||||
|-
|
||||
| Feature
|
||||
| Any of a Class's Attributes or Routines. Used similarly to the term Member as in C#. In Eiffel class features can be only Attributes or Routines.
|
||||
|-
|
||||
| Feature Application
|
||||
| The process of using the Class Features on instances of the Class. The model for Feature Application is "x.f(a,...)" where "x" is an entity which will be attached to some object at runtime, "f" is some Feature of the Class on which x is based, and "a,..." is an optional list of arguments which may be appropriate for some Routines. Feature "f" is "applied" to the object attached to "x". Feature Application may involve calling Routines, or may only involve returning the value of an Attribute from memory. Feature application is sometimes called "feature call".
|
||||
|-
|
||||
| Function
|
||||
| A Routine that returns a result. In C# function results can be typed as "void", meaning that there is no result returned. Eiffel functions cannot be typed "void" (see Procedure). All Functions must specify a result type. Of course, if the result type is a reference type, there could be cases in which the reference resulting from some specific call might be Void.
|
||||
|-
|
||||
| Generic Class
|
||||
| A Class which is written using parameters to represent other Classes or Types to enhance its reusability. For example, the Class LINKED_LIST [G] is a generic class in which "G" represents some other Class. So, LINKED_LIST is written independent of the items an instance of the list will hold. We cannot declare a LINKED_LIST without specifiying a Class to substitute for "G". If we want a list of cats we can declare one by using the Type: LINKED_LIST [CAT]. Generic Classes are not currently implemented in .NET, so there's no corresponding concept in C#.
|
||||
|-
|
||||
| Inheritance
|
||||
| One of the two Relationships that can exist between Classes. If Class B inherits from Class A, then every feature in Class A is potentially available to instances of Class B. Also, when ever an instance of Class A is required, an instance of Class B will suffice. Inheritance is used in Eiffel much the same as in C#. The Eiffel model allows full, controlled multiple inheritance, though. Eiffel Classes may inherit from multiple Concrete Classes and name clashes are handled through a process called Feature Adaptation.
|
||||
|-
|
||||
| Heir
|
||||
| The adjacent Proper Descendants of a Class. If class C inherits from class B and class B inherits from class A, then class A's Proper Descendants include B and C, but A's Heirs only include B.
|
||||
|-
|
||||
| Instance
|
||||
| A runtime Object of some type, based on the definition from a Class. Usually used in conjunction with the class name, e.g. "an instance of class POLYGON". In contrast with Direct Instance, an Instance of class POLYGON is a Direct Instance of POLYGON or any Proper Descendant of POLYGON such, perhaps, as RECTANGLE. So it its possible to have Objects which are Instances of Deferred Classes, but it is impossible to have Objects which are Direct Instances of Deferred Classes.
|
||||
|-
|
||||
| Interface
|
||||
| Not supported as such in Eiffel. Used in C# and VB.NET, Interfaces provide sets of specific Class Features which must be effected by any Concrete Class that "implements" the Interface. There is no concept of Interface in Eiffel that is separate from the concept of Class. So, Interfaces from .NET appear to Eiffel programmers to be completely Deferred (abstract) Classes.
|
||||
|-
|
||||
| Manifest string
|
||||
| A quoted string used in source code, for example the "Hello World!" in: <br/>
|
||||
my_string := "Hello World!" <br/>
|
||||
This is often called a "literal" string in other languages. In Eiffel, the quoted string "Hello World!" above is, by virtue of its presence, an instance of the Eiffel STRING class. In Eiffel there are manifest constants of other types as well, like numbers coded explicitly in software text, and the keywords True and False as the manifest booleans.
|
||||
|-
|
||||
| Module
|
||||
| A syntactical grouping of software text. Source code is divided into Modules in order to help organize it in some meaningful way or to achieve some goal.
|
||||
|-
|
||||
| Object
|
||||
| An instance of a Class during system execution. As such, Objects only exist at runtime.
|
||||
|-
|
||||
| Overloading
|
||||
| Not supported in Eiffel. Overloading is the ability to have more than one Feature with the same name, varying only by the Types of its Arguments. Overloading is supported by the underlying object model in .NET. As a result many classes in .NET assemblies have overloaded methods. The Eiffel Metadata Consumer disambiguates the overloaded names so that by the time you see them in the Eiffel context they appear no longer to be overloaded.
|
||||
|-
|
||||
| Parent
|
||||
| The adjacent Proper Ancestors of a Class. If class C inherits from class B and class B inherits from class A, then class C's Proper Ancestors include A and B, but C's Parents only include B.
|
||||
|-
|
||||
| Postcondition
|
||||
| An assertion coded on a Routine which defines the conditions will be true upon successful completion of the Routine. It is the responsibility of the Routine to ensure that the Postcondition holds after the Routine executes.
|
||||
|-
|
||||
| Precondition
|
||||
| An assertion coded on a Routine which defines the conditions under which the Routine can complete successfully. It is the responsibility of the Client to ensure that the Precondition holds before attempting to call the Routine.
|
||||
|-
|
||||
| Procedure
|
||||
| A Routine that does not return a result. Much like a "void" function in C#.
|
||||
|-
|
||||
| Proper ancestors
|
||||
| The set of all Classes from which a particular Class Inherits, excluding the Class itself.
|
||||
|-
|
||||
| Proper descendents
|
||||
| The set of all Classes which Inherit from a particular Class, excluding the Class itself.
|
||||
|-
|
||||
| Query
|
||||
| A Class Feature which returns information from the instance to which it is applied. Queries are the Reuse Consumer's view of Attributes and Functions.
|
||||
|-
|
||||
| Reference type
|
||||
| A type whose instances are accessed via a reference. For reference types, an object field holds a reference to a runtime object (or is Void). This is in contrast to value types, in which case, the object field holds the fields of the runtime object. In Eiffel, reference types are based on reference classes, i.e., any class which is not an Expanded Class.
|
||||
|-
|
||||
| Relationship
|
||||
| An association between two classes. There are only two types of Relationships that can exist between classes: Client/Supplier and Inheritance.
|
||||
|-
|
||||
| Reuse consumer
|
||||
| The role a software engineer assumes when in the process of making use of Classes or Types already in existence. As a reuse consumer, the engineer is interested primarily in the specification of those Classes or Types being used, rather than their implementation.
|
||||
|-
|
||||
| Reuse producer
|
||||
| The role a software engineer assumes when in the process or creating a new Class. In this role the engineer attempts to produce a product which will be reusable by him or herself and other engineers in the future. In the Eiffel culture, whenever a software engineer creates a class, it is as a Reuse Producer, and with the thought that each new class has the potential to become reusable.
|
||||
|-
|
||||
| Root class
|
||||
| For any System, the Class which will be instantiated to start the system. Specified by project settings.
|
||||
|-
|
||||
| Root procedure
|
||||
| A Creation Procedure of the Root Class of a System which will be applied to an initial instance of the Root Class at System startup. Similar to Main() in C# and VB.NET, but with the important difference that the fact that a particular Creation Procedure is the Root Procedure for a System is not define in the Class itself as with Main(). Rather, it is specified outside the Class in project settings. This helps Reuse Producers keep Classes more autonomous and therefore potentially more reusable.
|
||||
|-
|
||||
| Routine
|
||||
| A computational Class Feature which is either a Function or Procedure. Routines are the executable parts of a Class. Routines are similar to Methods in C#.
|
||||
|-
|
||||
| Software specification
|
||||
| A statement of how a software element is to be used and what it will do when executed. Ideally, this is stated in terms that do not betray how the software does what it does.In Eiffel, specification is included in the code, rather than a separate document. For a routine, it consists of its call signature and its contract. For a class, it consists of the signatures and contracts for all public features.
|
||||
|-
|
||||
| Supplier
|
||||
| A Class whose role is that of Supplier in a Client/Supplier Relationship with another Class.
|
||||
|-
|
||||
| System
|
||||
| A set of Classes related by the two Relationships which can be compiled to produce an executable.
|
||||
|-
|
||||
| Type
|
||||
| The description of a set of Objects equipped with certain Features.
|
||||
|}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[[Property:title|Eiffel reserved words]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|3b80ea3e-2ed1-adf6-ddf5-acd4e18c6d03]]
|
||||
The Eiffel programming language reserves certain words as meaningful to the Eiffel compiler.
|
||||
|
||||
To learn more about the reserved words specified by the current Eiffel standard, see the [[Eiffel programming language reserved words]] page of the [[Quick reference to the Eiffel programming language]].
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|Conventions]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|1f101597-06cd-b851-8cc1-e214b3eedb3e]]
|
||||
In this section, you'll find information about how the conventions that are normally used in Eiffel programming are affected by working in the presence of .NET.
|
||||
|
||||
Also, you'll find out how .NET types sometimes look a little different in the presence of Eiffel.
|
||||
|
||||
Because there are differences in the underlying object models upon which .NET and Eiffel have been designed, before .NET types can be made available to Eiffel developers, the assemblies in which they reside must be processed by a utility called the Eiffel Metadata Consumer. Fortunately, this all happens behind the scenes for you. You need only be aware that when you are looking at .NET types from within EiffelEnvision, you're seeing them through Eiffel-tinted lenses.
|
||||
|
||||
This is really the same thing that happens with other .NET languages with Visual Studio .NET support. For example, if you look at a constructor in a .NET type using the "ildasm" utility, the constructor will be named "<code>.ctor</code>". But, if you're in C# and look at the same constructor in the Visual Studio .NET Object Browser, the constructor will have the same name as the type ...it's a C# convention. Likewise, the same constructor viewed in Visual Basic .NET will have the name <code>New</code> ...that's the Visual Basic .NET convention. So, when you use EiffelEnvision, you see things as presented using Eiffel Conventions.
|
||||
|
||||
Consider what happens when you look at .NET types using EiffelEnvision. When you look, for example at the type <code lang="text">System.EventArgs</code>, you will see it represented as an Eiffel class called <code>EVENT_ARGS</code>. The members of the .NET type will show up as features of the Eiffel class. Of course, you can also see the corresponding .NET names as they exist in the assembly metadata.
|
||||
|
||||
The documents in this section tell you what you need to know in order to use .NET types in Eiffel, and vice versa.
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
[[Property:title|Similar Types Occurring in Both Libraries]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|4c9f6ad8-107b-af69-3eb3-3f076f2c936c]]
|
||||
==Whose String is it anyway?==
|
||||
|
||||
Over the last 15 years or so, the Eiffel class libraries have been a source for reusable software components for developers.
|
||||
|
||||
The Eiffel Base library contains classes for commonly used objects like different kinds of numbers, strings, files, and data structures.
|
||||
|
||||
But there are also libaries of Eiffel classes for sophisticated purposes like lexical analysis and parsing, data access, and graphical user interface development.
|
||||
|
||||
Likewise .NET is delivered with assemblies containing thousands of powerful types with similar purposes.
|
||||
|
||||
===Working in Both Worlds===
|
||||
|
||||
When we build software that has access to both the rich Eiffel libraries and the many useful .NET types, we inevitably run into types from both worlds that have similar names and purposes, but are still different types with different semantics.
|
||||
|
||||
===The Case of Strings===
|
||||
|
||||
The example of these similar types which will almost certainly get in your face is the string types. You may remember that we looked briefly at the case of the string types in Naming Conventions and Name Handling.
|
||||
|
||||
The Eiffel Base Library contains class <code>STRING</code>; the .NET assembly contains type <code>System.String</code>, which Eiffel for .NETusers see as <code>SYSTEM_STRING</code>. At an abstract level both of these model sequences of characters. But they are not the same type. In fact, they are different in some important ways. For example, instances of <code>System.String</code> are immutable. So you cannot append to an instance of <code>System.String</code>. If you want to build a string by appending, you should use an instance of <code>System.Text.StringBuilder</code> to do the appending, then extract the instance of <code>System.String</code> from it. With <code>STRING</code> it is permissible to append, so you don't need a helper like the <code>System.Text.StringBuilder</code> type.
|
||||
|
||||
So the two types are similar at an abstract level, but different semantically. There are reasonable arguments for the design of each.
|
||||
|
||||
Many types in the delivered assemblies have properties which are strings or methods which return or take strings as arguments. In all these cases, the strings in question are instances of <code>System.String</code>.
|
||||
|
||||
Many classes in the delivered Eiffel libraries have features involving strings, that is attributes which are strings or routines which return or take strings as arguments. In all these cases, the strings are instances of <code>STRING</code> (except for those designed for .NET compliance).
|
||||
|
||||
In C# and VB.NET, if you specify a quoted string like <code>"Hello World!"</code> in your code, that string will conform to type <code>System.String</code>. If you do the same in Eiffel, then <code>"Hello World!"</code> will be an instance of <code>STRING</code>. In Eiffel terminology, <code>"Hello World!"</code> appearing in source code is a manifest string.
|
||||
|
||||
What all this means to you is that you cannot use an instance of <code>System.String</code> when an instance of <code>STRING</code> is called for, and vice versa. Three out of four of the executable lines in the following code sample are invalid:
|
||||
<code>
|
||||
local
|
||||
my_string: STRING
|
||||
my_system_string: SYSTEM_STRING
|
||||
do
|
||||
my_system_string := "Hello World!" -- Invalid
|
||||
my_string := "Hello World!" -- Valid
|
||||
my_string := my_system_string -- Invalid
|
||||
my_system_string := my_string -- Invalid
|
||||
.
|
||||
.
|
||||
end
|
||||
</code>
|
||||
|
||||
To handle this issue, the Eiffel for .NET class <code>STRING</code> has two features which can be used when a string of the other type is needed.
|
||||
|
||||
The first of these features is a query <code>to_cil</code> which returns an object of type <code>System.String</code> which has a sequence of characters equivalent to that of the <code>STRING</code> to which <code>to_cil</code> is applied. The <code>to_cil</code> can be applied to manifest strings by enclosing the manifest string in parentheses.
|
||||
|
||||
The other feature is a creation procedure named <code>make_from_cil</code> which takes as an argument an instance of <code>System.String</code> and initializes its target <code>STRING</code> with a sequence of characters equivalent to that of the argument.
|
||||
|
||||
In the following sample, we use these features of <code>STRING</code> to make all the lines from the previous sample valid.
|
||||
<code>
|
||||
local
|
||||
my_string: STRING
|
||||
my_system_string: SYSTEM_STRING
|
||||
do
|
||||
my_system_string := ("Hello World!").to_cil -- Valid
|
||||
my_string := "Hello World!" -- Valid
|
||||
my_string.make_from_cil (my_system_string) -- Valid
|
||||
my_system_string := my_string.to_cil -- Valid
|
||||
.
|
||||
.
|
||||
end
|
||||
</code>
|
||||
|
||||
{{note|As shown in the above example, it is necessary to apply <code>to_cil</code> to a manifest string if you are assigning it to a <code>System.String</code> or passing it as an argument where a <code>System.String</code> is called for. <br/>
|
||||
This is expected to change in a future release. It shall become unnecesary to apply <code>to_cil</code> to manifest strings. Instead, whether a <code>STRING</code> or <code>System.String</code> is needed will be determined by the context in which the manifest string is being used, and the proper type of object will be generated. }}
|
||||
|
||||
===Other Similar Types===
|
||||
|
||||
There are many other cases of types available from the .NET assemblies which have similar purpose and semantics to those found in the Eiffel libraries. Fortunately, there is none that you will have to deal with as often as strings.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
[[Property:title|Type Organization]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|1837e091-695b-339c-f04f-cbfaea791a6c]]
|
||||
In any comprehensive object-oriented system, the act of programming results in creation of new data types. There must be a way of organizing these types and/or their static representation as classes. This section tells you how classes are organized in Eiffel and in .NET, and how these organization methods are used together.
|
||||
|
||||
==Eiffel Clusters==
|
||||
|
||||
Eiffel classes are grouped in clusters. These clusters of classes ordinarily share some commonality of functionality or purpose. In other words,the classes in a particular cluster may provide the same sorts of capabilities or relate to a single software model. In the Eiffel Base Library there is a cluster called <code>list</code> which contains classes which implement different types of lists, for example, <code>ARRAYED_LIST</code>, <code>LINKED_LIST</code>, <code>SORTED_TWO_WAY_LIST</code>, <code>TWO_WAY_CIRCULAR</code>. At an abstract level all these classes are related to the software model of the notion of "list".
|
||||
|
||||
The cluster <code>list</code> is actually a subcluster of the cluster <code>structures</code> which contains clusters other than <code>list</code> related to data structures other than lists. Eiffel convention dictates that a cluster should either contain classes or subclusters, but not both.
|
||||
|
||||
So clusters serve both to categorize and locate classes. So, class <code>LINKED_LIST</code> can be described as a basic library class implementing a data structure, more particularly a list. As such, it can be found in the Base Library, in cluster <code>structures</code> in subcluster <code>list</code>.
|
||||
|
||||
==.NET Namespaces and Assemblies==
|
||||
|
||||
In .NET, types (the language independent, compiled form of classes) are stored in "assemblies". So, we locate a type we want to use by referencing the assembly in which it resides.
|
||||
As .NET programmers, we think of types as being categorized by namespace. For example, we view the type <code>System.Windows.Forms.TextBox</code> as the <code>TextBox</code> type in the context of the windows forms namespace ( <code>System.Windows.Forms</code>). Particularly, this is in contrast to type <code>System.Web.UI.TextBox</code> which is a <code>TextBox</code> for web forms. As it relates to making .NET types usable by Eiffel, the important thing to understand is that the real .NET type name is the fully qualified type name, including the namespace. Namespaces are simply a bit of "syntactic sugar" that keeps us from having to repeat the entire type name every time we use it in source code.
|
||||
===.NET Assemblies Available to Eiffel===
|
||||
When types from .NET assemblies are made available to Eiffel programmers, each assembly is mapped to a cluster. So all the types in an assembly appear as if they were Eiffel classes in a cluster which corresponds to the .NET assembly.
|
||||
To summarize, as you learned in Naming Conventions and Name Handling, unambiguous Eiffel-style names are made available for the.NET types in an assembly. The assembly is represented to Eiffel for .NET programmers as a cluster of classes. The process of name derivation is based upon a portion of the .NET type name, possibly augmented with a prefix to ensure uniqueness.
|
||||
|
||||
===Assemblies Built with Eiffel===
|
||||
|
||||
The object model for which Eiffel was designed differs in some ways from the .NET object model. Importantly, Eiffel supports the facilties of full, controllable multiple inheritance, and genericity, among other things, that the inherent .NET object model does not. That does not mean that these things cannot work in .NET. Indeed they can, and they make Eiffel for .NET very powerful. But, they do make things look a little different.
|
||||
|
||||
When you compile Eiffel for .NET, the result is a .NET assembly; either an executable system, or a library of potentially reusable data types. Because the object model for Eiffel is different from that of .NET, the assembly resulting from a compile is different in some ways.
|
||||
|
||||
First an assembly built using Eiffel for .NET will likely contain lots of types and interfaces. This is because as you use a class from the Eiffel libraries, say class <code>STRING</code>, all the classes upon which <code>STRING</code> depends ( <code>STRING</code>'s suppliers and ancestors) must also be included in the assembly. That's because the Eiffel libraries, unlike the Microsoft .NET libraries like <code>mscorlib</code> and <code>System.Data</code>, are not distributed as shared type libraries.
|
||||
|
||||
Another thing you may notice is that each Eiffel class you produce is represented by three entities in the assembly ... two classes and an interface. So, if you produce a class called <code>GUARD_DOG</code>, then in the assembly you'd see an interface called <code>GuardDog</code>, a class called <code>Impl.GuardDog</code>, and a class called <code>Create.GuardDog</code>. Again, this is done for reasons that concern the differences in the object models between Eiffel and .NET.
|
||||
|
||||
The <code>GuardDog</code> interface is what you use when you declare an entity or variable of that type. The objects attached to that entity at runtime will be of the type <code>Impl.GuardDog</code>. You create an instance of <code>Impl.GuardDog</code> and attach it to an entity of type <code>GuardDog</code> by calling a routine in the factory class <code>Create.GuardDog</code>. The factory routines will almost always have names that begin with the word " <code>Make</code>", and represent the creation routines of Eiffel the classes. So in the case of using an instance of <code>GuardDog</code> from a C# class, the code would like this:
|
||||
<code>
|
||||
{
|
||||
GuardDog aGuardDog = Create.GuardDog.Make(); //Create an instance
|
||||
aGuardDog.RollOver(); // Apply a feature
|
||||
}
|
||||
</code>
|
||||
|
||||
This object creation model accounts for some of the differences between constructors in .NET and creation procedures in Eiffel. These differences will be discussed in more detail in Constructors and Creation Procedures.
|
||||
|
||||
Another advantage is that it provides a syntax that is similar to that used to create objects in Eiffel. An Eiffel for .NET client to the class <code>GUARD_DOG</code> might use the following code to create and use an instance.
|
||||
<code>
|
||||
local
|
||||
a_guard_dog: GUARD_DOG -- Declare an entity of the type
|
||||
do
|
||||
create a_guard_dog.make -- Create an instance and attach to entity
|
||||
a_guard_dog.roll_over -- Apply a feature
|
||||
end
|
||||
</code>
|
||||
|
||||
You may have noticed in these examples that even though the type <code>GuardDog</code> was compiled from an Eiffel class, when the C# client uses <code>GuardDog</code>, it uses what would be considered the .NET naming convention for the type <code>GuardDog</code> (vs. <code>GUARD_DOG</code>) and the method name <code>RollOver</code> (vs <code>roll_over</code>). What happens here is that when assemblies are produced from Eiffel classes, by default .NET naming standards are used in the assembly.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
[[Property:modification_date|Mon, 02 Oct 2023 12:50:47 GMT]]
|
||||
[[Property:publication_date|Mon, 02 Oct 2023 10:00:56 GMT]]
|
||||
[[Property:uuid|1838ECEC-F411-46C7-926B-204ED0ACD0C6]]
|
||||
[[Property:weight|20]]
|
||||
[[Property:title|Eiffel .NETCore execution]]
|
||||
[[Property:link_title|NETCore execution]]
|
||||
|
||||
|
||||
This document provide a quick overview on how to execute Eiffel.NetCore applications. It covers various aspects including the wrapper project, execution, publishing, debugging in VisualStudio, and known limitations
|
||||
|
||||
At the moment, the EiffelStudio '''debugger has no support''' Eiffel .NET Core.
|
||||
It is, however, possible to execute most of the Eiffel .NET Core application from the IDE using the `Run` command. The execution will be outside EiffelStudio.
|
||||
|
||||
The generated executable can not be executed by itself and requires to be launched using the `dotnet` tool from the command line.
|
||||
{{note|Use the command: `dotnet <app_name>` or `dotnet run <app_name>`.}}
|
||||
|
||||
== Wrapper project ==
|
||||
To help the user, the EiffelStudio compiler also generates a C# wrapper project (located in W_code or F_code directories). See the `wrapper_*.cs` and `wrapper_*.csproj` files.
|
||||
This simple C# '''wrapper''' project is simply a way to include and instantiate the Eiffel .NETCore code.
|
||||
|
||||
This "wrapper" project is useful for:
|
||||
* [[#Executing]] using profile
|
||||
* [[#Publishing|publishing]] the executable
|
||||
* [[#Debugging_in_VisualStudio|debugging]] using VisualStudio.
|
||||
* Working with any specific tool that requires a C# project
|
||||
|
||||
See the next sections for more information.
|
||||
|
||||
For an application named "app_netcore":
|
||||
* the C# code is quite simple:
|
||||
<code lang="cs">
|
||||
...
|
||||
public class wrap_app_netcore
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
MAIN.Main();
|
||||
}
|
||||
}
|
||||
</code>
|
||||
Note: the `MAIN.Main()` is the main entry point of the Eiffel .NETCore application.
|
||||
|
||||
* the `wrap_app_netcore.csproj` file looks like:
|
||||
<code lang="xml">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
None
|
||||
</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="app_netcore">
|
||||
<HintPath>app_netcore.exe</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EiffelSoftware.Runtime">
|
||||
<HintPath>Eiffelsoftware.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</code>
|
||||
|
||||
==Execution==
|
||||
It is possible that an application requires additional setting (for example web APIs application), in this case the `dotnet run` tool can use a specific '''profile''' using the syntax `--launch-profile <NAME>`.
|
||||
|
||||
Read more about the `dotnet` tool, and especially the `run` command at [https://learn.microsoft.com/fr-fr/dotnet/core/tools/dotnet-run] .
|
||||
|
||||
==Publishing==
|
||||
The '''wrapper''' project is helpful to '''publish''' the executable to the current or other platforms, and have an executable that does not require the `dotnet` command.
|
||||
It is also possible to include all dependencies (i.e: self contained) to ease deployment on machine without any dotnet runtime installed.
|
||||
|
||||
{{note| use the `dotnet publish` tool on the generated wrapper project. See the documentation about `dotnet publish` at [https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish] .}}
|
||||
|
||||
==Debugging in VisualStudio==
|
||||
Even if EiffelStudio does not provide any support for debugging Eiffel .NETCore directly from the IDE.
|
||||
|
||||
It is possible to use, for instance, '''VisualStudio''' to debug the generated Eiffel .NETcore application thanks to the wrapper C# project.
|
||||
|
||||
A solution is to use the C# wrapper project generated by the Eiffel compiler (in W_code or F_code directory) within VisualStudio.
|
||||
* Open the C# `.csproj` file with VisualStudio (it includes the associated Eiffel output dll or exe)
|
||||
* Configure the Debugging profile
|
||||
* Launch the execution with VisualStudio, and debug.
|
||||
|
||||
==Limitations==
|
||||
In the current version, the EiffelStudio compiler will always generate a Microsoft.NET.Sdk C# wrapper project. But this is not always the expected type SDKs.
|
||||
|
||||
For instance when using ASP.NETCore, it is expected to use `Microsoft.NET.Sdk.Web`.
|
||||
|
||||
In such case, the generated `.csproj` file needs to be manually modified to set the expected SDK, and also the various package dependencies.
|
||||
|
||||
For instance:
|
||||
* replace the first line of the `.csproj` file by <code lang="xml"><Project Sdk="Microsoft.NET.Sdk.Web"></code>
|
||||
* and in the `<ItemGroup>` section, list the package references such as <code lang="xml"><PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" /></code>
|
||||
|
||||
This allows the application to be run and, if necessary, debugged using VisualStudio.
|
||||
|
||||
{{info|To learn more, check the Eiffel .NETCore examples at [[Eiffel .NET samples]].}}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
[[Property:modification_date|Mon, 02 Oct 2023 13:38:24 GMT]]
|
||||
[[Property:publication_date|Mon, 02 Oct 2023 10:22:20 GMT]]
|
||||
[[Property:uuid|AF5801CA-928B-4870-BCC4-53DCAB23AF96]]
|
||||
[[Property:weight|10]]
|
||||
[[Property:title|Workaround Eiffel .NET limitations]]
|
||||
[[Property:link_title|Workarounds]]
|
||||
|
||||
There are a [[Eiffel for .NET Integration|few limitations]] in the integration between Eiffel .NET and .NET solution.
|
||||
However, it is sometime possible to find workaround, a common technique is to use a C# facade project, and use it from the Eiffel .NET project.
|
||||
|
||||
See in the following section, a way to use a facade C# project to workaround the current limitation on ".NET generic" compilation (i.e the fact the Eiffel compiler does not support yet the .NET generics).
|
||||
|
||||
This technique can be adapted to bypass other limitations or annoyances.
|
||||
|
||||
==Using generic .NET classes through a facade==
|
||||
Currently, Eiffel does not support consuming generics from C# classes. This tutorial demonstrates a workaround for this limitation by creating a Facade for a `List<string>` in C#
|
||||
|
||||
===Creating a facade for the List type===
|
||||
A Facade simplifies access to complex components. In this case, we will create a Facade to manage a list of strings. The Facade will encapsulate the list's functionality and expose a more straightforward interface. Here's how you can do it:
|
||||
|
||||
<code lang="cs">
|
||||
using System.Collections;
|
||||
namespace ListOfString;
|
||||
|
||||
/// <summary>
|
||||
/// Facade for a List<string> that encapsulates the list's functionality and exposes a few methods
|
||||
/// </summary>
|
||||
public class ListOfString
|
||||
{
|
||||
// Private list of strings
|
||||
private List<string> _list;
|
||||
|
||||
// Constructor that initialize the list
|
||||
public ListOfString()
|
||||
{
|
||||
_list = new List<string>();
|
||||
}
|
||||
|
||||
// Add an item to the list
|
||||
public void Add(string item)
|
||||
{
|
||||
_list.Add(item);
|
||||
}
|
||||
|
||||
// Check if the list contains a specific item
|
||||
public bool Contains(string item)
|
||||
{
|
||||
return _list.Contains(item);
|
||||
}
|
||||
|
||||
// Remove a specific item from the list
|
||||
public void Remove(string item)
|
||||
{
|
||||
_list.Remove(item);
|
||||
}
|
||||
|
||||
public IList GetList()
|
||||
{
|
||||
return _list.ToList();
|
||||
}
|
||||
}
|
||||
</code>
|
||||
|
||||
===Creating a C# library===
|
||||
To consume the Facade in Eiffel, we need to create a C# library. I recommend following the tutorial on creating a class library with C# and .NET on Microsoft’s official site. You can access it [https://learn.microsoft.com/en-us/dotnet/core/tutorials/library-with-visual-studio?pivots=dotnet-7-0 here]. This tutorial guides you through the process of creating a class library using C# and .NET.
|
||||
|
||||
===Consuming the C# library from Eiffel===
|
||||
Finally, we need to consume the C# library from Eiffel.
|
||||
|
||||
Open the Eiffel configuration file (.ecf) of your project and add the following entry
|
||||
|
||||
<code><assembly name="ListOfString" location="$PATH_CS_LIB\ListOfString.dll"/></code>
|
||||
|
||||
|
||||
===Conclusion===
|
||||
By creating a Facade for a `List<string>` in C#, we can effectively consume C# generic features in Eiffel.
|
||||
Obviously, this approach can be extended to other generic types as well.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
[[Property:modification_date|Mon, 23 Oct 2023 15:36:40 GMT]]
|
||||
[[Property:publication_date|Wed, 27 Sep 2023 19:33:07 GMT]]
|
||||
[[Property:title|Eiffel for .NET Integration]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|fe8a6a7d-4590-0db2-d59a-307082b18ecc]]
|
||||
==Differences between Eiffel and Eiffel for .NET==
|
||||
|
||||
===Limitation of Eiffel for .NET in version 23.09===
|
||||
|
||||
Most of the Eiffel mechanisms are supported in 23.09.
|
||||
|
||||
Eiffel for .NET supports:
|
||||
* Multiple Inheritance
|
||||
* Design By Contract
|
||||
* Exception handling
|
||||
* Genericity
|
||||
* Covariance
|
||||
* Compilation of any existing Eiffel libraries as long as it does not include C externals that call into the Eiffel Software C runtime
|
||||
* the [[Concurrent_programming_with_SCOOP|SCOOP]] capability is not support by the Eiffel.NET compiler
|
||||
|
||||
However, there are a few limitations that are described later in this documentation.
|
||||
|
||||
===Added to Eiffel and Eiffel for .NET===
|
||||
|
||||
The following syntax can be used to declare .NET custom attributes on Eiffel entities (features and classes):
|
||||
<code>
|
||||
empty: BOOLEAN
|
||||
note
|
||||
description: "Is Current empty?"
|
||||
metadata: create {OBSOLETE_ATTRIBUTE}.make ("Use `is_empty' instead") end
|
||||
obsolete
|
||||
"Use is_empty instead"
|
||||
do
|
||||
Result := is_empty
|
||||
end
|
||||
</code>
|
||||
|
||||
The previous example shows the declaration of the obsolete feature <code> empty </code> . The custom attribute defined by <code>OBSOLETE_ATTRIBUTE</code> is used to ensure that any consumer of the resulting assembly will see the feature as being obsolete. The custom attribute is defined in the <code>note</code> clause <code>metadata</code>. The definition consists of a creation expression that creates the custom attribute with the right parameters.
|
||||
|
||||
Using the <code>metadata</code> tag is the most general way of applying a custom attribute. There are however some variations that are explained below:
|
||||
*<code>metadata</code>: most general way, it applies a custom attribute to both the class and interface generated by the Eiffel compiler.
|
||||
*<code>class_metadata</code>: applies only to the class generated by the Eiffel compiler (mostly for advanced users).
|
||||
*<code>interface_metadata</code>: applies only to the interface generated by the Eiffel compiler (mostly for advanced users).
|
||||
*<code>property_metadata</code>: applies a custom attribute to the associated property generated by the Eiffel compiler for a query.
|
||||
*<code>assembly_metadata</code>: applies a custom attribute for the current assembly. It only works when present in the Eiffel system root class <code>note</code> clause.
|
||||
|
||||
==Differences between Eiffel for .NET and .NET==
|
||||
|
||||
===Covariance===
|
||||
The CLR (Common Language Runtime) does not support [[ET: Inheritance#Covariance and anchored declarations|covariance]] due to a different view of type safety (the issue is known known as a polymorphic [[ET: Inheritance#Catcalls|catcall]] in Eiffel). Catcalls are possible (although very rare) in Eiffel but not in .NET.
|
||||
|
||||
Eiffel for .NET implements a safe variant of covariance that will always perform a check on the types to avoid a catcall. So when a catcall is going to be performed a `Invalid Cast Exception` will be raised by the CLR instead of an unexpected behavior as is the default behavior in classic Eiffel (i.e., the behavior without catcall detection explicitly enabled).
|
||||
|
||||
Another advantage of Eiffel for .NET's implementation of covariance is that it can be easily understood by CLS-compliant consumer tools. These tools will actually benefit from the Eiffel for .NET covariance.
|
||||
|
||||
===Handling Eiffel and .NET genericity===
|
||||
As noted above, Eiffel for .NET fully supports the powerful genericity mechanism of the Eiffel language. The interface with .NET's own genericity mechanism is, however, not complete in version 23.09. Specifically:
|
||||
|
||||
* Generic classes in Eiffel will not yield generic classes in .NET. Instead, each generic derivation of a given Eiffel class will yield a different .NET class. So if we have a generic Eiffel class <code>LIST [G]</code>, the type <code>LIST [ANY]</code> will yield a .NET class <code>LIST_ANY</code>, and the type <code>LIST [INTEGER]</code> will yield a class <code>LIST_Int32</code> .
|
||||
|
||||
* If you want to use a generic .NET class (for example in C#), you have to use special techniques as described next.
|
||||
|
||||
{{Recommended|Read the [[Workaround Eiffel .NET limitations]] page to learn how to workaround such limitation. }}
|
||||
|
||||
===Enum types===
|
||||
Eiffel for .NET supports .NET enum types implicitly. From the point of view of Eiffel, they are just considered as expanded classes. The only difference is in the code generation. Eiffel for .NET cannot declare new enum types yet.
|
||||
|
||||
===ByRef===
|
||||
|
||||
Eiffel does not have the notion of `byref` argument passing. At the moment, Eiffel for .NET cannot call nor can it redefine a feature that has a byref argument.
|
||||
|
||||
=== Type with "init" only setters===
|
||||
Eiffel currently does not support the special `init` property that is only initialized in a block of code as part of the object initialization. More details can be found in the [https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/init C# 9.0 proposal]
|
||||
|
||||
Note: {{inline-info|this section is mostly for .NETCore 6.0 and above}}.
|
||||
|
||||
'''Workaround''':
|
||||
|
||||
A potential workaround is to create a C# library that uses a Factory pattern to build the required instance.
|
||||
|
||||
===Execution and publishing ===
|
||||
Currently the EiffelStudio debugger supports only the Eiffel .NET Framework application (i.e .Net framework v4.0).
|
||||
|
||||
For .NETCore (net6.0, net7.0, ...) it is not yet available within the EiffelStudio IDE.
|
||||
|
||||
{{recommended|Learn more how to publish, execute, debug, ... the Eiffel .NETCore application, with the [[Eiffel .NETCore execution]] page.}}
|
||||
|
||||
== Environment variables for .NETCore ==
|
||||
The Eiffel compiler sets a few environment variables available in the ECF files and allows to write ECF file without hardcoded version numbers.
|
||||
|
||||
* '''ISE_DOTNET_FRAMEWORK''': location of the default .NET runtime directory (parent of System.dll, System.Runtime.dll, ...)
|
||||
* '''ISE_DOTNET_PLATFORM''': for a .NET project it contains either "framework", or "netcore" to distinguish between .NET Framework (v4.0) and .NETCore solutions (net6.0, neet7.0, ...)
|
||||
* '''ISE_DOTNET_PACKS''': location to the .NETCore "packs" directory (parent of .NETCore '''reference'' runtimes)
|
||||
* '''ISE_DOTNET_PACKS''': location to the .NETCore "shared" directory (parent of .NETCore implementation runtimes)
|
||||
* '''ISE_DOTNET_TFM''': .NETCore TFM (Target Framework Moniker) , for instance "net6", "net7", ...
|
||||
* '''ISE_DOTNET_VERSION''': .NETCore runtime precise version number (for instance 7.0.11)
|
||||
@@ -0,0 +1,535 @@
|
||||
[[Property:title|Adding Class Features]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|04261c22-b28f-b045-a5d7-2c85efe992b9]]
|
||||
The features of a class make it useful. They are the things that objects which are instances of the class have and can do.
|
||||
|
||||
It is during the process of adding class features that we relate a class we are producing to other classes via the client/supplier relationship.
|
||||
|
||||
It is when we add features to a class that we can build the executable code that makes things happen.
|
||||
|
||||
==Categorizing Features==
|
||||
|
||||
In Eiffel, we have several ways of thinking abstractly about features and categorizing them. As you saw in [[Eiffel Classes|Eiffel Classes]] the <code>feature</code> clause gives us a way to group features in the software text. We have ways to group features more generally, too. Here are some.
|
||||
|
||||
===Source===
|
||||
|
||||
You remember the example class from [[Eiffel Classes|Eiffel Classes]] :
|
||||
<code>
|
||||
note
|
||||
description: Objects that model lists
|
||||
revision: $Revision: 1.5 $
|
||||
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
|
||||
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
||||
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create an empty list.
|
||||
do
|
||||
before := True
|
||||
ensure
|
||||
is_before: before
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end
|
||||
|
||||
first: like item
|
||||
-- Item at first position
|
||||
do
|
||||
Result := first_element.item
|
||||
end
|
||||
|
||||
... other features omitted ...
|
||||
|
||||
invariant
|
||||
before_constraint: before implies (active = first_element)
|
||||
after_constraint: after implies (active = last_element)
|
||||
end
|
||||
</code>
|
||||
|
||||
|
||||
The example shows three of the features ( <code>make</code>, <code>item</code>, and <code>first</code>) coded directly in the class. These features (and the ones omitted from the example in the interest of brevity) may not be the only features of the class <code>OLD_FASHIONED_LIST</code>. In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class:
|
||||
<code>
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
</code>
|
||||
|
||||
This means that every feature in <code>DYNAMIC_LIST</code> will be a feature of <code>OLD_FASHIONED_LIST</code>. So one way we can think of features is by their source.
|
||||
* Immediate features are those that are introduced by a class itself.
|
||||
* Inherited features are those that come to the class from its proper ancestors.
|
||||
|
||||
We will see more about this in [[Inheritance|Inheritance]] .
|
||||
|
||||
===Implementation Type===
|
||||
|
||||
Whenever we are building a class in Eiffel, we are potential reuse producers. As such, we can categorize the features of a class based on the three types of feature implementation:
|
||||
* Attribute
|
||||
* Function
|
||||
* Procedure
|
||||
|
||||
Attributes are those features which occupy storage in an instance. When you code an attribute in a class that you are producing, that class becomes a client to the class of the attribute. This is the most common way to establish a client/supplier relationship between the classses. Client/supplier is one of the two relationships that can exist between classes.
|
||||
|
||||
Functions and procedures are the computation features, that is they are features that involve executable code. Functions and procedures together are themselves categorized as routines.
|
||||
|
||||
Also, from the producer standpoint we may view features as whether they work from memory or through computation:
|
||||
* Memory
|
||||
** Attribute
|
||||
|
||||
* Computation
|
||||
** Function
|
||||
** Procedure
|
||||
|
||||
|
||||
This view can be valuable when a producer is considering performance issues. For example, if there is some class function called <code>count</code>, a producer might investigate whether it is better to leave <code>count</code> as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often.
|
||||
|
||||
===Usage===
|
||||
|
||||
A times we find it appropriate to categorize features by how we use them. Specifically, as:
|
||||
* Query
|
||||
** Attribute
|
||||
** Function
|
||||
|
||||
* Command
|
||||
** Procedure
|
||||
|
||||
|
||||
Queries are features that, when applied to an instance, provide a value in response. Commands instruct an intance to take some action, but do not return a value. Seeing features as either queries or commands is of primary interest to reuse consumers. But as producers there are important reasons for ensuring that when we implement a feature, we implement it as a query or as a command, but not both. We will see more about this in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
==General Syntax of Features==
|
||||
|
||||
===More Sample Features===
|
||||
|
||||
Here is another example class. Again this class contrived, so it does not do anyhing worthwhile except show you what different types of features look like. This class has an attribute and a constant attribute, and functions and procedures both with and without arguments.
|
||||
<code>
|
||||
class
|
||||
SOME_CLASS
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_arguments
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Creation procedure
|
||||
do
|
||||
an_attribute := 5
|
||||
end
|
||||
|
||||
make_with_arguments (hour: INTEGER; minute: INTEGER; second: INTEGER)
|
||||
-- Another creation procedure
|
||||
do
|
||||
an_attribute := second + (minute * 60) + (hour * 3600)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
an_attribute: INTEGER
|
||||
-- An attribute of type INTEGER
|
||||
|
||||
another_attribute: INTEGER = 46
|
||||
-- A constant attribute
|
||||
|
||||
a_function: STRING is
|
||||
-- A function without arguments
|
||||
do
|
||||
Result := an_attribute.out
|
||||
end
|
||||
|
||||
another_function (an_int: INTEGER): INTEGER
|
||||
-- A function with arguments
|
||||
do
|
||||
Result := an_attribute + an_int
|
||||
end
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
a_procedure
|
||||
-- A procedure with no arguments
|
||||
do
|
||||
an_attribute := an_attribute + 5
|
||||
end
|
||||
|
||||
another_procedure (an_int: INTEGER; another_int: INTEGER)
|
||||
-- A procedure with arguments
|
||||
do
|
||||
an_attribute := an_attribute + an_int + another_int
|
||||
end
|
||||
|
||||
end -- Class SOME_CLASS
|
||||
</code>
|
||||
|
||||
===Feature Declaration===
|
||||
|
||||
When you write a feature in a class, you typically will include some of the following:
|
||||
* The feature's name
|
||||
{{note|In Eiffel every feature of a class must have a name that is unique within that class. }}
|
||||
|
||||
* The feature's type (in the case of an attribute or a function)
|
||||
* The feature's formal argument list (in the case of a function or procedure that has arguments)
|
||||
* The actual value of the feature (in the case of a constant attribute)
|
||||
* The implementation code (in the case of a function or procedure)
|
||||
|
||||
Let's dissect one feature and identify its parts:
|
||||
<code>
|
||||
another_function (an_int: INTEGER): INTEGER
|
||||
-- A function with arguments
|
||||
do
|
||||
Result := an_attribute + an_int
|
||||
end
|
||||
</code>
|
||||
|
||||
In this feature:
|
||||
* "another_function" is the feature's name.
|
||||
* "(an_int: INTEGER)" is the argument list.
|
||||
** "an_int" is the name of the first argument
|
||||
** "INTEGER" is the type "an_int"
|
||||
|
||||
* "INTEGER" (after the argument list) is the feature's type.
|
||||
* "do " introduces the implementation code.
|
||||
* "Result := an_attribute + an_int" is an instruction.
|
||||
|
||||
{{note|This feature is a function. As a consequence the computation uses the keyword "<eiffel>Result</eiffel>' as an entity name for the value to be returned by the function }}
|
||||
|
||||
* "<eiffel>end</eiffel>" ends the feature declaration
|
||||
|
||||
==General Structure of Routines==
|
||||
|
||||
As you can imagine, the possibilities for coding routines are more complex than those for coding attributes. Routines always contain the keyword "is" after the first part of the feature declaration. The part after the "is" is called the routine part.
|
||||
|
||||
The routine part is made up of the following sections:
|
||||
* [[#Header Comment|Header Comment]]
|
||||
* [[#obsolete|Obsolete]]
|
||||
* [[#Precondition|Precondition]]
|
||||
* [[#Local Declarations|Local Declarations]]
|
||||
* [[#Routine Body|Routine Body]]
|
||||
* [[#Postcondition|Postcondition]]
|
||||
* [[#Rescue|Rescue]]
|
||||
|
||||
All of the sections except the Routine Body are optional.
|
||||
|
||||
Here is another feature, a routine, which has all of these except obsolete and rescue.
|
||||
<code>
|
||||
insert_text_header_item (a_label: STRING;
|
||||
a_width, a_format: INTEGER; insert_after_item_no: INTEGER)
|
||||
-- Insert a text item to the header control
|
||||
-- after the `insert_item_item_no' item.
|
||||
require
|
||||
exists: exists
|
||||
label_not_void: a_label /= Void
|
||||
insert_after_item_no_positive: insert_after_item_no >= 0
|
||||
local
|
||||
hd_item: WEL_HD_ITEM
|
||||
do
|
||||
create hd_item.make
|
||||
hd_item.set_text (a_label)
|
||||
hd_item.set_width (a_width)
|
||||
hd_item.set_format (a_format)
|
||||
insert_header_item (hd_item, insert_after_item_no)
|
||||
ensure
|
||||
item_count_increased: item_count = old item_count + 1
|
||||
end
|
||||
</code>
|
||||
|
||||
Using this example, let's identify and discuss the different sections.
|
||||
|
||||
===Header Comment===
|
||||
<code>
|
||||
-- Insert a text item to the header control
|
||||
-- after the `insert_item_item_no' item.
|
||||
</code>
|
||||
|
||||
Although the feature's header comment is optional, most Eiffel programmers and their technical managers feel that it should never be omitted. Header comments are included by some language processing tools in specialized views of classes. Header comments are almost always short, usually no more than a few lines. Header comments serve to provide a natural language description of what the feature will do (in the case of features that are commands) or what information it provides (in the case of queries).
|
||||
|
||||
===Obsolete===
|
||||
|
||||
The feature <code>insert_text_header_item</code> is not obsolete ... but if it were it would include an <code>obsolete</code> clause. This works much like the obsolete part for classes that we saw in [[Eiffel Classes|Eiffel Classes]] : the keyword "<code>obsolete</code>" followed by a manifest string which bears a message to potential reuse consumers:
|
||||
|
||||
===Precondition===
|
||||
<code>
|
||||
require
|
||||
exists: exists
|
||||
label_not_void: a_label /= Void
|
||||
insert_after_item_no_positive: insert_after_item_no >= 0
|
||||
</code>
|
||||
|
||||
The precondition part of a feature is introduced by the keyword "<code>require</code>". It contains a set of assertions which define the state necessary for the correct execution of a routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
===Local Declarations===
|
||||
<code>
|
||||
local
|
||||
hd_item: WEL_HD_ITEM
|
||||
</code>
|
||||
|
||||
This part contains the declarations for any "local entities" used by the feature. Sometimes the computation accomplished in a feature requires the use of entities which are only temporary. It would not be appropriate to make these attributes of the class. So, instead we can use local entities, which have scope only within the feature in which they are declared. In the example, <code>hd_item</code> is available as type <code>WEL_HD_ITEM</code> during the computation of feature <code>insert_text_header_item</code>.
|
||||
|
||||
{{note|A local entity name must not be the same as any feature of the class in which its feature occurs or the same as any argument name of the feature in which it occurs. }}
|
||||
|
||||
===Routine Body===
|
||||
<code>
|
||||
do
|
||||
create hd_item.make
|
||||
hd_item.set_text (a_label)
|
||||
hd_item.set_width (a_width)
|
||||
hd_item.set_format (a_format)
|
||||
insert_header_item (hd_item, insert_after_item_no)
|
||||
</code>
|
||||
|
||||
This is the routine body for a fairly typical effective, internal routine. It contains the instructions that get the job done for the feature. You may notice that experienced Eiffel programmers rarely write routine bodies that are more than a few lines long. The reason for this is more that just intuitive. This phenomenon will be explained in the section [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
There are other forms that a routine body can take. Here are some examples of some others:
|
||||
|
||||
====External Routines====
|
||||
<code>
|
||||
cwin_tooltips_class: POINTER
|
||||
external
|
||||
"C [macro ] : EIF_POINTER"
|
||||
alias
|
||||
"TOOLTIPS_CLASS"
|
||||
end
|
||||
</code>
|
||||
|
||||
The routine body above is for an "external" routine. External routines are used to represent within Eiffel classes, routines that are written in other languages.
|
||||
|
||||
{{tip|Because of the high degree of language interaction provided by Microsoft.NET, it is not necessary in Eiffel for.NET to use externals to use software components from.NET assemblies. Instead, these components are presented to the Eiffel programmer as if they were Eiffel classes. Read more about this in [[Conventions|Conventions]] . }}
|
||||
|
||||
====Once Routines====
|
||||
<code>
|
||||
once
|
||||
Result := some_computation
|
||||
</code>
|
||||
|
||||
This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword "<code>once</code>" rather than <code>do</code>. It contains an computational body that executes only the first time it is called. Upon subsequent calls it has no effect.
|
||||
|
||||
Once procedures are useful for doing things that for some reason should not be done multiple times.
|
||||
|
||||
If the once routine is a function (as the example above), it computes the resulting object on the first call, then on subsequent calls, it returns the object it originally created without executing the computation. Once functions facilitate shared objects which helps us in avoiding global entities. A class containing a once function can be inherited by many other classes. The first object to apply the once function causes the resulting object to be created and initialized. Subsequent applications by any other object accesses the originally created instance.
|
||||
|
||||
====Deferred Routines====
|
||||
<code>
|
||||
deferred
|
||||
</code>
|
||||
|
||||
The body for a deferred routine is simply the keyword "<code>deferred</code>". Below is the deferred routine body in the context of an entire feature.
|
||||
<code>
|
||||
set_position (new_position: INTEGER)
|
||||
-- Set `position' with `new_position'
|
||||
require
|
||||
exists: exists
|
||||
valid_minimum: new_position >= minimum
|
||||
valid_maximum: new_position <= maximum
|
||||
deferred
|
||||
ensure
|
||||
position_set: position = new_position
|
||||
end
|
||||
</code>
|
||||
|
||||
As we learned in [[Eiffel Classes|Eiffel Classes]] a deferred routine has specification, but no implementation. We will investigate deferred classes and features further in [[Inheritance|Inheritance]] .
|
||||
|
||||
===Postcondition===
|
||||
<code>
|
||||
ensure
|
||||
item_count_increased: item_count = old item_count + 1
|
||||
</code>
|
||||
|
||||
The postcondition part of a routine is introduced by the keyword "<code>ensure</code>". The postcondition is a group of assertions which describe the state that must be satisfied upon the successful completion of the routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
===Rescue===
|
||||
|
||||
Our example feature does not have an explicitly coded rescue part. The rescue, introduced by the keyword "<code>rescue</code>", provides a routine with a chance to do additional processing in the case that it incurs an exeption during normal processing. We will learn about the rescue clause in the section [[Exception Mechanism|Exception Mechanism]] . Until then, you can see what a rescue part looks like in the feature below.
|
||||
<code>
|
||||
bind
|
||||
-- Bind socket to local address in `address'.
|
||||
require
|
||||
socket_exists: exists
|
||||
valid_local_address: address /= Void
|
||||
local
|
||||
ext: ANY
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
ext := address.socket_address
|
||||
c_bind (descriptor, $ext, address.count)
|
||||
is_open_read := True
|
||||
end
|
||||
rescue
|
||||
if not assertion_violation then
|
||||
is_open_read := False
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
==Making Features Available to Clients==
|
||||
|
||||
Remember that when we build classes in Eiffel, we keep in mind the possibility that these classes may eventually become valued, reusable software components. As a result we have a responsibility to make sure that our classes allow clients to use them in a safe and productive fashion. We make available those features that it is appropriate for clients to use, and we hide the rest. In Eiffel we say that a feature that is available to clients is "exported".
|
||||
|
||||
Control of the export of features inherited from ancestors is done by using the "export" keyword. Export of inherited features will be discussed in Inheritance.
|
||||
|
||||
Control of the export of immediate features (those features introduced in the text of a class) is done with the "feature" clause. We have encountered the feature clause already:
|
||||
<code>
|
||||
feature -- Access
|
||||
|
||||
an_attribute: INTEGER
|
||||
-- An attribute of type INTEGER
|
||||
|
||||
another_attribute: INTEGER = 46
|
||||
-- A constant attribute
|
||||
|
||||
a_function: STRING
|
||||
-- A function without arguments
|
||||
do
|
||||
Result := an_attribute.out
|
||||
end
|
||||
|
||||
another_function (an_int: INTEGER): INTEGER
|
||||
-- A function with arguments
|
||||
do
|
||||
Result := an_attribute + an_int
|
||||
end
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
a_procedure
|
||||
-- A procedure with no arguments
|
||||
do
|
||||
an_attribute := an_attribute + 5
|
||||
end
|
||||
|
||||
another_procedure (an_int: INTEGER; another_int: INTEGER)
|
||||
-- A procedure with arguments
|
||||
do
|
||||
an_attribute := an_attribute + an_int + another_int
|
||||
end
|
||||
</code>
|
||||
|
||||
A feature clause like the ones in the example above means that all the features that follow, until the next feature clause are exported to clients based on any class. Technically, this
|
||||
<code>
|
||||
feature -- Basic Operations
|
||||
</code>
|
||||
|
||||
is equivalent to
|
||||
<code>
|
||||
feature {ANY} -- Basic Operations
|
||||
</code>
|
||||
|
||||
which means that the following features are available to clients which conform to class <code>ANY</code>. And in Eiffel, <code>ANY</code> is the class from which all other classes inherit. As a consequence all classes conform to <code>ANY</code>.
|
||||
|
||||
Inside the braces is a list of classes which are eligible as clients.
|
||||
<code>
|
||||
feature {STRING_HANDLER} -- Implementation
|
||||
</code>
|
||||
|
||||
Features following this example from class <code>STRING</code> will be available to client class <code>STRING_HANDLER</code> and all its proper descendants.
|
||||
|
||||
As stated above, you can put a list of class names in the braces:
|
||||
<code>
|
||||
feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features
|
||||
</code>
|
||||
|
||||
Often features which are solely for implementation should not be seen or used by clients of any type. You can ensure this by exporting to class <code>NONE</code>, the class from which no other class can inherit:
|
||||
<code>
|
||||
feature {NONE} -- Implementation
|
||||
</code>
|
||||
|
||||
==Eiffel Instructions and Control Structures==
|
||||
|
||||
When you begin to write routines in Eiffel, you will need to understand how to write instructions. Fortunately, the set of instruction types you can code is fairly small. Here we will look the most common of these. You will see some more in other topics.
|
||||
|
||||
===Creation===
|
||||
<code>
|
||||
create hd_item.make
|
||||
</code>
|
||||
|
||||
We discussed creation procedures and the process of bringing new objects into being in [[Eiffel Classes|Eiffel Classes]] . A creation instruction starts with the keyword "<code>create</code>". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity.
|
||||
|
||||
===Procedure Call===
|
||||
<code>
|
||||
hd_item.set_text (a_label)
|
||||
</code>
|
||||
|
||||
The application of a feature to an object constitutes an instruction if the feature is a procedure.
|
||||
|
||||
===Assignment===
|
||||
|
||||
In Eiffel, assignment syntax is simple. But depending upon the types involved, what actually happens may need some explanation. Assume here that <code>is_open_read</code> is an attribute declared as type <code>BOOLEAN</code>:
|
||||
<code>
|
||||
is_open_read := False
|
||||
</code>
|
||||
|
||||
In this instruction, an attribute of type <code>BOOLEAN</code> named <code>is_open_read</code> is being assigned the value of the manifest boolean "<code>False</code>". The attribute <code>is_open_read</code> is based on an expanded class <code>BOOLEAN</code>, so that means that the value for <code>False</code> is held in the field for <code>is_open_read</code>. This is in contrast to what happens with reference types.
|
||||
<code>
|
||||
my_string := some_other_string
|
||||
</code>
|
||||
|
||||
In this assignment, we will assume that both entities are type <code>STRING</code>. Because <code>STRING</code> is a reference type, the field for <code>my_string</code> will hold either a reference to an instance of <code>STRING</code>, or it will be <code>Void</code>. When the assignment above executes, then whatever was in the field for <code>my_string</code> is replaced with a reference to the same object that <code>some_other_string</code> refers to.
|
||||
|
||||
In summary, for objects based on expanded classes, assignment means assignment of value. For objects based on reference classes, assignment means assignment of reference.
|
||||
|
||||
====Conformance====
|
||||
|
||||
There is an important rule about assignment in Eiffel. The rule exists to ensure type safety. Consider the following assignment which we would read as "<code>x</code> receives <code>y</code>" or "<code>x</code> gets <code>y</code>".
|
||||
<code>
|
||||
x := y
|
||||
</code>
|
||||
|
||||
then the rule says that this assignment is valid only if the type of <code>y</code> conforms to the type of <code>x</code>.
|
||||
|
||||
The rule is clear enough ... if you know what "conform" means. In the presence of mechanisms like expanded types, inheritance, and genericity, a precise definition of conformance is lengthy.
|
||||
|
||||
But we can get by for a while with much less. For now let us be satisfied with this:
|
||||
|
||||
Consider the classes in the declarations for <code>x</code> and <code>y</code>.
|
||||
|
||||
You might be tempted to say that to achieve "conformance", the classes would have to be the same. And for the case in which the declarations for <code>x</code> and <code>y</code> are expanded classes, you'd be correct. They conform only if they are the same class.
|
||||
|
||||
But for reference types, that constraint is unnecessarily restrictive. So, for reference types, the class of <code>y</code> conforms to that <code>x</code> if it is the same class or a proper descendant of that class. This facilitates a powerful idea known as polymorphic attachment that we will see more closely in the section on [[Inheritance|Inheritance]] .
|
||||
|
||||
===Conditional===
|
||||
<code>
|
||||
if l_c.is_digit then
|
||||
l_state := 2
|
||||
elseif l_c = '-' or l_c = '+' then
|
||||
l_state := 1
|
||||
else
|
||||
l_state := 3
|
||||
end
|
||||
</code>
|
||||
|
||||
Conditionals in Eiffel use at a minimum the keywords "<code>if</code>", "<code>then</code>", and "<code>end</code>". Optionally, they may use the keywords "<code>elseif</code>" and "<code>else</code>".
|
||||
|
||||
===Loop===
|
||||
<code>
|
||||
from
|
||||
i := lower
|
||||
until
|
||||
i > upper
|
||||
loop
|
||||
if item (i) /= Void and then v.is_equal (item (i)) then
|
||||
Result := Result + 1
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
</code>
|
||||
|
||||
There is only one structure for loops in Eiffel. In its typical form it uses the four keywords "<code>from</code>", "<code>until</code>", "<code>loop</code>", and "<code>end</code>". The instructions following the <code>from</code> keyword do any initialization necessary for the loop. After <code>until</code> is a boolean expression which is the exit condition. The loop body after <code>loop</code> is a list of instructions that are executed for every iteration. As you can imagine, something in the loop body should most likely cause the exit condition eventually to become true.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
[[Property:title|Design by Contract and Assertions]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|41172f82-227a-96b1-2dad-624f04374ee0]]
|
||||
==Motivation: Concerning Correctness==
|
||||
|
||||
When you produce an element of software, how do you know that what you produced is correct?
|
||||
|
||||
This is a difficult question for anyone to answer. Informally speaking, correct software is software that does what it is supposed to do. That is what makes answering the question so tricky. Before you can have any idea whether the software is correct, you must be able to express what it is supposed to do ... and that proves to be quite difficult itself.
|
||||
|
||||
In conventional software engineering, a document called a software specification is written in order to describe what it is that a piece of software is supposed to do. Writers of software specifications tend to pursue one of two approaches: the informal or the formal.
|
||||
|
||||
Informal specifications attempt to describe software behavior in the natural languages with which humans communicate on a daily basis. There are problems with this approach. Natural language is not precise. Informal specifications are subject to interpretation and affected by the ambiguities, noise, and contradiction inherent in natural language.
|
||||
|
||||
In order to avoid these problems, proponents of formal methods of specification turn to the most precise language they know: mathematics. It may be no exaggeration that the study of formal methods has produced more PhD's in Computer Science than it has well-specified software systems. Still the idea that the precision of mathematics can be brought to bear on the problem of specifying software is quite appealing. But, problems lurk here as well. Formal specifications are difficult and time-consuming to construct and verify against themselves, and most software engineers do not have a working knowledge of the mathematics required to work with formal specifications.
|
||||
|
||||
There is one more significant problem with both of these approaches. Even if you have a very precise specification, expressed in elegant text and graphics, and placed carefully in an expensive ring binder, how do you know that the software product actually reflects that specification and vice versa? If either is changed, the other must be as well. This is the document synchronization problem.
|
||||
|
||||
===Design by Contract to the Rescue===
|
||||
|
||||
Design by Contract (DbC) begins as an implementation of some of the ideas from formal methods and matures into a powerful way of thinking about software. And it does it in a way that is easy for programmers and managers to understand. DbC also puts the software specification into the software document itself which makes it checkable at runtime and eliminates the document synchronization problem.
|
||||
|
||||
==Model for Software Correctness==
|
||||
|
||||
Design by Contract is built around a model for software correctness that is really pretty simple.
|
||||
|
||||
Suppose there is software routine called <code>s</code>. If we were going to test <code>s</code>, we would probably devise some test inputs or test values to be in place when <code>s</code> starts and then observe what things look like after <code>s</code> completes. If they look the way we think they should then that leads us to believe that S is working correctly for those test inputs.
|
||||
|
||||
We can generalize and formalize that process a bit, taking it back from testing an into design. If indeed we know what it means for <code>s</code> to be correct, then we should be able to make a statement of any conditions that must be true prior to executing <code>s</code>. That is, we will state the conditions required for it to be possible for <code>s</code> to run correctly. We call this statement of preconditions for success <code>s</code>'s precondition.
|
||||
|
||||
Likewise we should be able to make a statement of the conditions that will be true always if <code>s</code> works correctly. This we call <code>s</code>'s postcondition.
|
||||
|
||||
As an example, suppose <code>s</code> accepted an argument of type <code>REAL</code> and returned another <code>REAL</code> which was the square root of the argument. The precondition for <code>s</code> would be that the argument could not be less that zero, as there is no real square root for negative numbers. <code>s</code>'s postcondition would be that the result multiplied by itself would yield the value of the original argument (give or take a little to allow for floating point error).
|
||||
|
||||
==Assertions in Eiffel==
|
||||
|
||||
Each Eiffel feature which is a routine, i.e. a function or procedure, can support one assertion for a precondition and one for a postcondition. We saw where precondition and postcondition fit into the structure of the routine in [[Adding Class Features|Adding Class Features]] . An assertion is expressed as one or more assertion clauses which are logically <code>and</code>-ed together to produce the assertion. Assertions clauses are boolean expressions that evaluate to true or false.
|
||||
|
||||
Let's look at another example. Assume you need to produce a class to model a time of day. Each instance of <code>TIME_OF_DAY</code> would hold some particular time of day accurate to the second between 00:00:00 and 23:59:59 inclusive.
|
||||
|
||||
As a producer, you would be faced with a decision concerning how to maintain the time internally in each instance. For the purpose of our example, let us consider two alternatives:
|
||||
# Keep three instance of <code>INTEGER</code>. One each for hour, minute, and second.
|
||||
# Keep one instance of <code>INTEGER</code> representing the time of day as the number of seconds since 00:00:00.
|
||||
|
||||
This would be an implementation issue for the producer, because it would not affect the services that <code>TIME_OF_DAY</code> could offer clients. If we have a query called <code>minute</code> the first alternative allows us simply to provide the value from storage. Whereas the second alternative will likely cause us to compute <code>minute</code> each time it is requested. But the service looks and works the same for the client in either alternative.
|
||||
|
||||
For now let us assume that we are using the first design alternative. In that case we would code class features for <code>hour</code>, <code>minute</code>, and <code>second</code>.
|
||||
<code>
|
||||
feature -- Access
|
||||
|
||||
hour: INTEGER
|
||||
-- Hour expressed as 24-hour value
|
||||
|
||||
minute: INTEGER
|
||||
-- Minutes past the hour
|
||||
|
||||
second: INTEGER
|
||||
-- Seconds past the minute
|
||||
</code>
|
||||
|
||||
Below is the code for a procedure <code>set_second</code> which receives an argument of type <code>INTEGER</code> and sets the value of the <code>second</code> feature to the argurment.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
do
|
||||
second := s
|
||||
end
|
||||
</code>
|
||||
|
||||
The routine is simple enough, but there is a problem with it. Suppose a client calls <code>set_second</code> with an argument whose value is invalid, say 3574. Our routine would just stuff this value into <code>second</code> and we would end up with an instance of <code>TIME_OF_DAY</code> which is invalid. In the days before Design by Contract, as soon as we recognized that this problem exists, we would go into "defensive programming" mode and code some "<code>if</code>" statements inside the routine to validate the argument, before acting.
|
||||
|
||||
Consider though what we can do with Design by Contract. We will add a precondition assertion to <code>set_second</code> that expresses the need for valid arguments.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
require
|
||||
valid_argument_for_second: 0 <= s and s <= 59
|
||||
do
|
||||
second := s
|
||||
end
|
||||
</code>
|
||||
|
||||
The precondition is introduced by the keyword "<code>require</code>". The text "<code>valid_argument_for_second</code>" is the label for the assertion clause. The boolean expression "<code>0 <= s and s <= 59</code>" says that a good value for <code>s</code> will be between 0 and 59 inclusive.
|
||||
|
||||
Remember that the precondition specifies those things that must be true if <code>set_second</code> has a chance of working correctly. As such, upon execution, the body of this routine will never be executed if an attempt is made to call it in a state that does not meet its precondition. Instead, the caller will incur a precondition violation exception. We will investigate more about what exceptions mean further in [[Exception Mechanism|Exception Mechanism]] .
|
||||
|
||||
So, what about a postcondition? We noted earlier that a postcondition should make a statement of what will be true in the case that the routine does its work correctly. For <code>set_second</code> this means that after it finishes, the query <code>second</code> should have the same value as the argument that was received from the caller. Below is the feature with the postcondition added.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
require
|
||||
valid_argument_for_second: 0 <= s and s <= 59
|
||||
do
|
||||
second := s
|
||||
ensure
|
||||
second_set: second = s
|
||||
end
|
||||
</code>
|
||||
|
||||
The postcondition is introduced by the keyword "<code>ensure</code>". Here the expression "<code>second = s</code>" makes certain that the routine did actually do the necessary work to ensure that the value of <code>second</code> matches the value of the argument received.
|
||||
|
||||
As you look at the postcondition, you may be tempted to think that the one-line body of the routine is so simple as to make the postconditon unnecessary. To answer this concern we need to look again for a moment at software specification.
|
||||
|
||||
===Specification of a Routine===
|
||||
|
||||
If we remove the instructions from a routine and leave its signature, header comment and assertions, we have a specification for the routine.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
require
|
||||
valid_argument_for_second: 0 <= s and s <= 59
|
||||
ensure
|
||||
second_set: second = s
|
||||
end
|
||||
</code>
|
||||
|
||||
This specification of <code>set_second</code> tells us what is required of reuse consumers if they wish to use <code>set_second</code> and what <code>set_second</code> promises to do for them. Importantly, it does that without revealing how it does it does what it does.
|
||||
|
||||
So, this specification view, officially called the contract view, is how consumers of class <code>TIME_OF_DAY</code> would view the feature.
|
||||
|
||||
If this is the specification, then haven't we put the cart before the horse? The answer is yes. We have done so to illustrate the problems that assertion-based specification can help solve.
|
||||
|
||||
Instead of starting with a routine and adding a specification, we really want to start in accepted software engineering fashion with specification first, and then add implementation. Therefore, the specification you see above would exist before the implementation.
|
||||
|
||||
Now back to the concern over whether the postcondition is redundant for this simple one-line routine. Obviously, if this specification exists first, then the postcondition must be there, and it would be silly to remove it later. But, more importantly, suppose that when the producer of the class decided on an implementation, he or she chose the second design alternative we mentioned above. This would mean that the internal state of an instance of <code>TIME_OF_DAY</code> would be only one <code>INTEGER</code> with the number of seconds since midnight. That would mean that the query <code>second</code> would probably be a function that computed seconds from that one <code>INTEGER</code> instead of an attribute.
|
||||
|
||||
What would change in the specification of <code>set_second</code>? Nothing. The implementation for the routine would be more complex, but what it does, setting the second in an instance of <code>TIME_OF_DAY</code>, would stay the same.
|
||||
|
||||
In summary, the precondition and postcondition ensure for use that the routine will only execute if called in a state in which the precondition is true, and then will either complete in a state in which the postcondition is true, or cause a postcondition violation exception.
|
||||
|
||||
===The Contract for a Routine===
|
||||
|
||||
Having assertions on its routines forms a contract between the <code>TIME_OF_DAY</code> class and all potential reuse consumers. The contract is much like a contract in business, with obligations and benefits for both parties.
|
||||
* The client's benefits are outlined in the postcondition.
|
||||
* The client's obligations come from the precondition.
|
||||
* The supplier's obligations are in the postcondition.
|
||||
* The supplier's benefits come from the precondition.
|
||||
|
||||
We can see the specifics of these by using <code>set_second</code> as an example.
|
||||
* The client gets the desired value for seconds set in the instance.
|
||||
* The client must provide an argument that is valid for seconds.
|
||||
* The supplier must update the instance successfully.
|
||||
* The supplier need not risk disaster attempting to process in an invalid state nor waste time validating the argument received from the client.
|
||||
|
||||
===Valid State for Instances===
|
||||
|
||||
Assertions on <code>TIME_OF_DAY</code>'s routines gives us specification for each routine and guarantees that if the specification of a routine gets violated at runtime we will be served immediately with an exception. This will go a long way toward preventing invalid instances from going unnoticed in a running system and fouling up lots of other stuff.
|
||||
|
||||
What will help even more is something called a class invariant. Using the class invariant we are able to state what it means for an instance of a class to be valid, or as it is sometimes put, in a stable state.
|
||||
|
||||
The class invariant is an assertion like precondition and postcondition, but there is only one per class. Check [[Eiffel Classes|Eiffel Classes]] to see how the class invariant fits into the class structure.
|
||||
|
||||
How would we code the class invariant for <code>TIME_OF_DAY</code>? An instance would be valid if its hour were between 0 and 23 inclusive, minute were between 0 and 59 inclusive, and its second were between 0 and 59 inclusive. We can code the invariant as shown below.
|
||||
<code>
|
||||
invariant
|
||||
hour_valid: 0 <= hour and hour <= 23
|
||||
minute_valid: 0 <= minute and minute <= 59
|
||||
second_valid: 0 <= second and second <= 59
|
||||
</code>
|
||||
|
||||
The name invariant implies that the assertion can never be false ... and that's true up to a point. It's really more like, "it must be true at times when it really counts".
|
||||
|
||||
At runtime the invariant must be true for an instance at anytime that the instance is available to clients. In general, this means that the invariant must be true before and after the execution of every exported routine.
|
||||
|
||||
As with the assertions on routines, if ever the invariant is not true when it should be, then a class invariant violation occurs.
|
||||
|
||||
Remember in the example above, that the features <code>hour</code>, <code>minute</code>, and <code>second</code> are queries, but they could be either attributes or functions.
|
||||
|
||||
==The Contract for a Class==
|
||||
|
||||
Earlier we saw the contract for a routine. Now we can define the contract for a class as the aggregation of the contracts for all its exported features, plus its class invariant.
|
||||
|
||||
In Design by Contract we design based on these contracts. They are the specifications for the modules in our system. We work in a reuse-oriented world. Whenever we produce a class, we produce it with a comprehensive contract which serves as its specification. We build each class with the thought that it may eventually become reusable.
|
||||
|
||||
When we are in our reuse consumer role, using existing classes, we tend not to look at the implementations for the classes we use. Instead we look at their contract views. It is there that we find the obligations and benefits of using each class.
|
||||
|
||||
==Contracts and Debugging==
|
||||
|
||||
We saw earlier that having contracts in the code tends to expose bugs at an early stage of development. It is possible selectively to turn off and on the runtime checking of assertions by changing project settings. Checking assertions does involve processing. More about turn off assertion checking in a moment.
|
||||
|
||||
Having contracts on a class gives another advantage when the contract gets broken. The contract tells us whose fault it is. Whenever there is a violation of a precondition, postcondition, or class invariant then the software is out of specification. This situation is called a defect, or bug.
|
||||
|
||||
Whose fault is it? If a precondition was violated, then a client class attempted to call a routine in a supplier, but made the call in a state that did not satisfy the supplier's precondition. Therefore the bug is in the client.
|
||||
|
||||
If a postcondition was violated, then a client made a call in a state that did satisfy the supplier's precondition, but the supplier was unable to complete the work as agreed. Therefore the fault lies with the supplier.
|
||||
|
||||
If a class invariant was violated, then the instance has been placed in an invalid state during the execution of a routine, and left that way when the processing completed. This caused the invariant violation. As with the postcondition violation, because the problem occurred while executing routines in the supplier, preconditions must have been met. The supplier then is to blame.
|
||||
|
||||
Based on this knowledge, we can say that it is most practical first to turn off runtime checking of postconditions and invariants as we gain confidence in a class. Meaning of course, that we feel confident that any calls that meet preconditions will be properly processed. Then our only worry is that some deranged client will, with total disregard for our carefully crafted preconditions, make calls to our routines from invalid states. So, maybe we will leave precondition checking turned on for a while.
|
||||
|
||||
==Contracts and Inheritance==
|
||||
|
||||
In the section titled [[Inheritance|Inheritance]] you saw that it was possible through inheritance to produce a new class that has all the features of an existing one. This is a very powerful notion, and could be dangerous. What would keep descendants from redefining inherited features with semantics that were totally different from those intended by the producer of the original class? Nothing if it were not for the contract.
|
||||
|
||||
Simply speaking assertions on a parent class, preconditions, postconditions, and class invariants, all are inherited by the class's proper descendants.
|
||||
|
||||
For class invariants, if any new invariants are coded in an heir, they will be added to those inherited from the parent, using a non-strict version of logical "and" (We will define non-strict booleans in Writing Assertions below).
|
||||
|
||||
That is simple enough. And the situation is also simple for effective routines inherited and left unchanged ... the contracts stand as written.
|
||||
|
||||
From our example above you may have gotten the idea that contracts are really useful only for effective routines. Such is not the case. In fact, specifying a contract on a deferred routine is really a powerful notion. It says not only that effective descendants must provide an implementation for this routine, but also that there is a contract that must be satisfied. Effecting or redefining a routine in a descendant class will not make the contract go away. Here is an feature from the Base Library deferred class <code>ACTIVE</code> which models data structures with a current item, and is an ancestor to many effective container type classes.
|
||||
<code>
|
||||
feature -- Element change
|
||||
|
||||
replace (v: G)
|
||||
-- Replace current item by `v'.
|
||||
require
|
||||
writable: writable
|
||||
deferred
|
||||
ensure
|
||||
item_replaced: item = v
|
||||
end
|
||||
</code>
|
||||
|
||||
Feature <code>replace</code> carries the semantics necessary for replacing an item in an <code>ACTIVE</code>. It does not, however provide an implementation. All implementers must produce versions of <code>replace</code> that satisfy the contract specified here.
|
||||
|
||||
It actually is possible to alter a feature assertion in an effected or redefined version(technically its a replacement of the original version):
|
||||
* The precondition can only become weaker than in the inherited contract.
|
||||
* The postcondition can only become stronger than in the inherited contract.
|
||||
|
||||
These rules are imposed as a consequence of the effect of effected or redefined routines on polymorphism and dynamic binding. But, you can understand them from an intuitive viewpoint, if you reconsider the business contract analogy. Suppose a contractor makes a deal with a client to do certain work (represented by the postcondition). Part of the deal might be that the client agrees to have a site ready by a certain date (represented by the precondition). The contractor represents the parent class in the analogy. Now suppose the contractor brings in a subcontractor (representing the heir class) to do a portion of the work. The subcontractor cannot force the client to change the date that the site is to be ready to an earlier date (no strengthing of the precondition). The deal with the client was made by the contractor and so no new or stronger requirements can be imposed by the subcontractor. Likewise the subcontractor must provide at least as much work as was bargained for by the contractor, but may promise to provide more if appropriate (strengthing of postcondition is allowed.)
|
||||
|
||||
In Writing Assertions below you will see the syntax for weaking preconditions and strengthening postconditions.
|
||||
|
||||
==Unfinished Business==
|
||||
|
||||
In the section [[Adding Class Features|Adding Class Features]] , we promised to explain two issues during this discussion of Design by Contract.
|
||||
|
||||
===Short Routines===
|
||||
|
||||
One of these is the tendency of mature Eiffel programmers to write routines that are quite short. It should be clear by now that we wish to build a contract on each routine. The contract describes the semantics of the routine in a declarative fashion. In other words, it tells what the routine does, without giving an indication of how it does it.
|
||||
|
||||
Try to imagine giving a declarative description of a routine that was 50 lines long. This could get ugly. So experienced Eiffel developers decompose complex computations into chunks small enough to be described through a clear, concise set of assertions.
|
||||
|
||||
===Command/Query Separation===
|
||||
|
||||
In [[Adding Class Features|Adding Class Features]] , we saw that we can categorize features as either queries or commands. A query will ask a question or make an observation about an instance. A command will tell the instance to take some action which may result in changing the instances internal state.
|
||||
|
||||
We said that when program routines, that those routines should either be commands or queries but not both. Importantly, asking a question about an instance should not change the instance. Likewise, taking an action that changes the state of an instance should not return a result.
|
||||
|
||||
Here's the rationale. In a routines postcondition we use boolean expressions to ensure that the routine has done its job properly. Likewise, class invariants, which define the valid state for instances, are written as boolean expressions. In both cases we may use the features of the class which are queries to ask about an instances current state.
|
||||
|
||||
If a query that we use in an assertion were to change the state of the instance, then the result we received would be invalid as soon as we received it.
|
||||
|
||||
Therein lies the primary reasoning behind command/query separation. You cannot reason about the integrity of an object if the act of asking a question changes the object.
|
||||
|
||||
==Writing Assertions==
|
||||
|
||||
You have seen fairly typical assertions written in the examples above. Study the classes in the libraries to see some excellent working examples. There are a couple of things that need to be covered.
|
||||
|
||||
===Non-Strict Booleans===
|
||||
|
||||
One is that, as you can probably imagine, it is not a good thing to cause an exception during the process of checking an assertion. One of the most common ways to cause such an exception is to apply a feature to a Void reference.
|
||||
|
||||
The way to avoid this is to use the non-strict booleans "<code>and then</code>" and "<code>or else</code>". These forms of "<code>and</code>" and "<code>or</code>" do not force the checking of all conditions. As soon as a determination can be made, they stop checking. It is typical to see "<code>and then</code>" used to avoid applying a feature to a void reference in preconditons. Below is a creation procedure that uses a non-strict boolean in its precondition.
|
||||
<code>
|
||||
make (a_nm: STRING; a_offset: INTEGER)
|
||||
-- Initalize with name `a_nm' and utcoffset `a_offset'.
|
||||
require
|
||||
name_not_empty: a_nm /= Void and then not a_nm.is_empty
|
||||
offset_valid: a_offset >= -12 and a_offset <= 12
|
||||
do
|
||||
name := a_nm.twin
|
||||
utcoffset := a_offset
|
||||
ensure
|
||||
name_initialized: name.is_equal (a_nm)
|
||||
utcoffset_initialized: utcoffset = a_offset
|
||||
end
|
||||
</code>
|
||||
|
||||
===Replacing Inherited Feature Assertions===
|
||||
|
||||
To replace a precondition on a feature you are effecting or redefining, you use the "<code>require else</code>" keywords to introduce new conditions. These conditions will be logically "<code>or</code>-ed" with the original precondition to form an new one.
|
||||
|
||||
Likewise use "<code>and then</code>" to add conditions to a postcondition. The added conditions will be "<code>and</code>-ed" to the original.
|
||||
|
||||
Below is an example of weakening a precondition. The first feature shown is from class <code>DYNAMIC_CHAIN</code> in the Base Library.
|
||||
<code>
|
||||
remove_left
|
||||
-- Remove item to the left of cursor position.
|
||||
-- Do not move cursor.
|
||||
require
|
||||
left_exists: index > 1
|
||||
deferred
|
||||
ensure
|
||||
new_count: count = old count - 1
|
||||
new_index: index = old index - 1
|
||||
end
|
||||
</code>
|
||||
|
||||
The next feature is from <code>DYNAMIC_LIST</code>, a proper descendant of <code>DYNAMIC_CHAIN</code>. <code>DYNAMIC_LIST</code> weakens the precondition it inherited from <code>DYNAMIC_CHAIN</code>. Originally in <code>DYNAMIC_CHAIN</code>, "<code>index > 1</code>" was required for <code>remove_left</code>. In <code>DYNAMIC_LIST</code> either "<code>index > 1</code>" or "<code>not before</code>" (or both) will suffice.
|
||||
<code>
|
||||
remove_left
|
||||
-- Remove item to the left of cursor position.
|
||||
-- Do not move cursor.
|
||||
require else
|
||||
not_before: not before
|
||||
deferred
|
||||
end
|
||||
</code>
|
||||
|
||||
==Not Writing Assertions==
|
||||
|
||||
Let's close this discussion of Design by Contract with one more interesting and point to make about assertions. The precondition and postcondition parts of a routine are optional, as you may remember from [[Adding Class Features| Adding Class Features]] . Suppose you write a routine and do not code either precondition or postcondition. You might be tempted to think that you have simply written a routine that has no contract. But, that would not be the case.
|
||||
|
||||
The contract exists, even though you do not code it explicitly. If it were written out, it would look as follows.
|
||||
<code>
|
||||
my_routine
|
||||
-- My descriptive header comment
|
||||
require
|
||||
True
|
||||
ensure
|
||||
True
|
||||
end
|
||||
</code>
|
||||
|
||||
What does this mean? It means that you have selected the weakest possible precondition and postcondition for your routine. Of course, this may be perfectly valid under some circumstances.
|
||||
|
||||
Just understand that if your routine could speak, it would be telling you, "I can always work successfully without any particular guarantees from you at all. On the other hand, I won't promise you any particular results when I get done."
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
[[Property:title|Eiffel Classes]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|f905426e-3109-b60b-adf0-c74b83c81b55]]
|
||||
The unit of software reuse in Eiffel is the class.
|
||||
|
||||
The unit of modularity in Eiffel is the class.
|
||||
|
||||
The unit of type modeling in Eiffel is the class.
|
||||
|
||||
All Eiffel code must exist within the context of a class.
|
||||
|
||||
In Eiffel, application systems, or simply systems, are created by assembling a set of related classes. The classes in a system will be related only by one or both of the two allowable relationships in object-oriented design.
|
||||
|
||||
Having read the above, you should be convinced that the concept of class is important and far-reaching. The fact that we have precise rules about classes simplifies life a lot. The only kind of module in Eiffel is a class. Each class exists in one source file (which contains only that class), and contains the code necessary to provide a static definition of a data type. Every runtime entity, i.e. every object, must be an instance of a class. Because we can depend upon these things in Eiffel, we have consistency and predictabililty in the inherently complex world of software development.
|
||||
|
||||
Let's take a look at how classes are structured.
|
||||
|
||||
The code that makes up an Eiffel class is divided into the following parts:
|
||||
|
||||
==Structure of a Class==
|
||||
|
||||
All of the above, except Class header, are optional. So the simplest Eiffel class you could build would look like this:
|
||||
<code>
|
||||
class
|
||||
SIMPLE
|
||||
end
|
||||
</code>
|
||||
|
||||
Okay, so class SIMPLE is only interesting in its simplicity. Let's look at an example that is more illustrative:
|
||||
<code>
|
||||
note
|
||||
description: Objects that model lists
|
||||
revision: $Revision: 1.4 $
|
||||
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
|
||||
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
||||
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create an empty list.
|
||||
do
|
||||
before := True
|
||||
ensure
|
||||
is_before: before
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end
|
||||
|
||||
first: like item
|
||||
-- Item at first position
|
||||
do
|
||||
Result := first_element.item
|
||||
end
|
||||
|
||||
... other features omitted ...
|
||||
|
||||
invariant
|
||||
before_constraint: before implies (active = first_element)
|
||||
after_constraint: after implies (active = last_element)
|
||||
</code>
|
||||
Here is a class that, although completely contrived, utilizes all of the required and optional parts of the class. Let's look at each part individually.
|
||||
===Note===
|
||||
<code>
|
||||
note
|
||||
description: Objects that model lists
|
||||
revision: $Revision: 1.4 $
|
||||
</code>
|
||||
|
||||
The <code>note</code> part of a class is there to allow you as a producer to record information of your choice which will help you or other reuse consumers at some later time to locate understand the class. This important in Eiffel because we try to treat every class as if someday it will become reusable.
|
||||
|
||||
Information in <code>note</code> does not change the semantics of the class.
|
||||
|
||||
The <code>note</code> part in the class above is typical. It is introduced with the language keyword <code>note</code>, and contains two note clauses, each of which is comprised of an index and a single index value. You can code note clauses with indexes that you devise yourself, so there is nothing inherently special about "<code>description</code>" and "<code>revision</code>" as used above. But, these indexes could be special to tools which analyze libraries of classes use them. Although these clauses have only one index value each, it is permissible to put more, separated by commas.
|
||||
|
||||
===Class Header===
|
||||
<code>
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
</code>
|
||||
|
||||
The class header is introduced by the keyword "class", which in turn can be preceded by one of three keywords which mark the class as <code>deferred</code>, <code>expanded</code>, or <code>frozen</code>. In our example, the class has none of these markings, so it is an effective class whose instances are access by reference.
|
||||
|
||||
The keyword class is followed by the class name, in this case "<code>OLD_FASHIONED_LIST</code>".
|
||||
|
||||
Of the three keywords for header marks, the one which you will encounter most often is <code>deferred</code>. A class is deferred if it contains one or more features that are deferred, that is, features which have been specified in the class but for which no implementation has been provided. Proper descendants of a deferred class will provide implementations for its deferred features.
|
||||
|
||||
===Formal Generics===
|
||||
<code>
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
</code>
|
||||
|
||||
In this example the class name is followed by the specification of one formal generic parameter "<code>G</code>". The presence of one or more formal generic parameters will designate a class as a generic class. The formal generic parameter is a place holder for a class name which will be provided by reuse consumers. For example if we wrote a class which was a client to <code>OLD_FASHIONED_LIST</code> we would substitute the class name for the type of objects that we would want to build an <code>OLD_FASHIONED_LIST</code> of. We might make this declaration:
|
||||
<code>
|
||||
my_list_of_cats: OLD_FASHION_LIST [CAT]
|
||||
</code>
|
||||
|
||||
The entity <code>my_list_of_cats</code> could then be attached at runtime to an <code>OLD_FASHIONED_LIST</code> of objects of type <code>CAT</code>. So the class <code>CAT</code> becomes an actual generic parameter and substitutes for <code>G</code> in the declaration.
|
||||
|
||||
Of course formal generic parameters cannot be the same name as a class name in the same universe. If multiple formal generic parameters are used, they are separated by commas.
|
||||
|
||||
You will learn more about generic classes in the section titled [[Genericity|Genericity]] .
|
||||
|
||||
===Obsolete===
|
||||
<code>
|
||||
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
||||
</code>
|
||||
|
||||
<code>OLD_FASHION_LIST</code>s are obsolete ... and the class is marked as such by include the line above. The manifest string contains an explanation, instructions, and/or recommended alternatives. Compilers and other language tools can deliver this message to potential reuse consumers. As with <code>note</code>, <code>obsolete</code> has no effect on the semantics of the class.
|
||||
|
||||
Obsolete is rarely used because of the nature of certain elements of the Eiffel methodology. For example, if implementations are well-hidden behind implementation-independent specifications, then those implementations may be changed to adapt the class to changing execution environments in such a way that clients are unaffected.
|
||||
|
||||
===Inheritance===
|
||||
<code>
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
</code>
|
||||
|
||||
One of the two possible relationships between classes, inheritance is also a powerful software reuse mechanism. In this example class <code>OLD_FASHIONED_LIST</code> declares itself to be a proper descendant of class <code>DYNAMIC_LIST</code>.
|
||||
|
||||
There will be more in the section called . For now though, be aware of two important implications of this declaration:
|
||||
* Every feature of <code>DYNAMIC_LIST</code> is available to <code>OLD_FASHIONED_LIST</code> and potentially available to its clients.
|
||||
* Whenever an instance of <code>DYNAMIC_LIST</code> is called for, then an instance of <code>OLD_FASHIONED_LIST</code> will suffice.
|
||||
|
||||
===Creators===
|
||||
<code>
|
||||
create
|
||||
make
|
||||
</code>
|
||||
|
||||
The creators part of a class declares a procedure as being a creation procedure. In this case the procedure in question is the one named <code>make</code>. By convention, creation procedure names begin with the word " <code>make</code>".
|
||||
|
||||
Let's take a quick look at object creation. Consider this declaration:
|
||||
<code>
|
||||
my_list_of_cats: OLD_FASHION_LIST [CAT]
|
||||
</code>
|
||||
|
||||
Here the entity <code>my_list_of_cats</code> can be attached to an object of type <code>OLD_FASHION_LIST [CAT]</code> at runtime. The process of converting <code>my_list_of_cats</code> from holding a void reference to holding a reference to a object modeling a list of cats, starts when a creation instruction is executed. The creation instruction creates the instance and may apply a creation procedure to initialize the instance. A creation instruction for the declaration above would look like this:
|
||||
<code>
|
||||
create my_list_of_cats.make
|
||||
</code>
|
||||
|
||||
The <code>create</code> keyword is used to introduce a creation instruction. This instruction causes the following four things to happen:
|
||||
* A shell of a new instance of <code>OLD_FASHION_LIST [CAT]</code> is created in memory with a memory field for every attribute
|
||||
* Each field is initialized with standard default values
|
||||
** False for type <code>BOOLEAN</code>
|
||||
** Null character for type <code>CHARACTER</code>
|
||||
** The appropriate form of zero for number types
|
||||
** <code>Void</code> for reference types
|
||||
|
||||
* Attach the new instance to the entity <code>my_list_of_cats</code>
|
||||
* Apply the creation procedure <code>make</code>
|
||||
|
||||
Once these steps complete successfully, <code>my_list_of_cats</code> will be attached to a valid instance (i.e., an instance in which the class invariant is true) of <code>OLD_FASHIONED_LIST [CAT]</code>.
|
||||
|
||||
===Features===
|
||||
<code>
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create an empty list.
|
||||
do
|
||||
before := True
|
||||
ensure
|
||||
is_before: before
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end
|
||||
|
||||
first: like item
|
||||
-- Item at first position
|
||||
do
|
||||
Result := first_element.item
|
||||
end
|
||||
</code>
|
||||
|
||||
The features part of a class is the area in which we feel that most of the "programming" is done. It is here that we define those things that instances of a class have and can do. We will learn more about features in the next section [[Adding Class Features|Adding Class Features]] .
|
||||
|
||||
Until then let's just take a quick look at how features fit into a class. Notice that in our example the features part is introduced by the keyword "<code>feature</code>". In fact there are two occurrences of <code>feature</code> in this example, each followed by a comment.
|
||||
|
||||
You may declare multiple <code>feature</code> statements. This helps you group features in a manner that makes sense. Here we see the first group contains those features which are listed as creation procedures in the creators part of the class. The second group of features labeled "<code>Access</code>" contains a set of queries available to clients of the class.
|
||||
|
||||
Although the words "<code>Initialization</code>" and "<code>Access</code>" are actually in comments after the <code>feature</code> keyword, some language processing tools apply some significance to these, for example, ordering the groups in "pretty-printed" views of a class. Also, some tools allow you to build templates for creating new classes which have <code>feature</code> clauses already in place for predetermined groups.
|
||||
|
||||
{{tip|There is not a technical requirement governing the grouping or ordering of features in a class. It is the option of the producer of a class to group and order the features in a fashion that holds some meaning. Many years of Eiffel development experience are reflected in the classes in the EiffelBase Library. This is a good place to look for examples of well constructed classes. }}
|
||||
|
||||
|
||||
===Invariant===
|
||||
<code>
|
||||
invariant
|
||||
before_constraint: before implies (active = first_element)
|
||||
after_constraint: after implies (active = last_element)
|
||||
</code>
|
||||
|
||||
|
||||
Here's the last word in a class definition ... both literally and figuratively. The invariant part, introduced not surprisingly by the keyword "<code>invariant</code>", is that portion of the class in which we can state what it means for an object to be a valid instance of this class.
|
||||
|
||||
We will learn more about class invariants in the section titled [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[[Property:title|Eiffel for .NET Compliance]]
|
||||
[[Property:weight|9]]
|
||||
[[Property:uuid|1e19c2f0-995e-02c1-0588-c134a11e0003]]
|
||||
As of Eiffel for ASP.NET 5.6 and EiffelStudio 5.7; Eiffel for .NET introduces the notion of '''Eiffel-Compliance'''. .NET specifies a number of language interoperability rules in a [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconwhatiscommonlanguagespecification.asp Common Language Specification-Compliance] specification. Eiffel for .NET supports all CLS-Compliant type and features in .NET but now additionally supports a number of non-CLS-compliant types and features. This is the purpose of the Eiffel-Compliant notion.
|
||||
|
||||
The information contained within this page does not go into any depth on the Common Language Specification (CLS) or CLS-Compliance. For this information please see Microsoft's on-line documentation [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconwhatiscommonlanguagespecification.asp What is the Common Language Specification?]
|
||||
|
||||
<span id="applicablity"></span>
|
||||
==Applicability==
|
||||
|
||||
The CLS states a number of rules .NET tools have to abide by to be CLS-compliant. These rules relate to both a .NET producer, generally a compiler generating .NET code, and a consumer, which is generally a compiler that consumes .NET assemblies for reuse. Eiffel-Compliance relates only to consumption of .NET assemblies.
|
||||
<span id="what_is_compliant"></span>
|
||||
==What is Eiffel-Compliant?==
|
||||
|
||||
As already stated, anything CLS-compliant is Eiffel-compliant. Eiffel-Compliancy merely allows for exceptions so that non-CLS-compliant assemblies, types and members can be used in Eiffel for .NET.
|
||||
|
||||
The following list outlines the supported non-CLS-compliant types:
|
||||
* All unsigned numerical basic types such as <eiffel>System.UInt32</eiffel> as <eiffel>System.UInt64</eiffel>, represented by <eiffel>NATURAL_xx</eiffel>.
|
||||
* Native pointers (<eiffel>System.IntPtr</eiffel>), represented by <eiffel>POINTER</eiffel>
|
||||
|
||||
|
||||
Typically assemblies, types or members are marked with the <eiffel>System.CLSCompliantAttribute</eiffel> attribute to explicitly indicate if they are CLS-compliant or not. The Eiffel for .NET compiler will now ignore this attribute and instead infer if they are Eiffel-Compliant. With such an inference engine at hand, Eiffel for .NET opens itself up to extended support for COM/Legacy interop as well support assemblies not adhering to CLS-compliant rules, for one reason or another.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
[[Property:title|Event Programming with Agents]]
|
||||
[[Property:weight|7]]
|
||||
[[Property:uuid|ea9d7b9d-daa1-a415-628a-e3a2f935c12a]]
|
||||
In Eiffel there is a facility referred to as agents.
|
||||
|
||||
The implementation of agents is an advanced topic, but you do not have to understand the details of the implementation of agents to put agents to work for you. That is what you will learn in this section.
|
||||
|
||||
==Objects that Represent Operations==
|
||||
|
||||
Object technology is based on the idea that when we model systems based on objects, representing the "things" they manipulate. As to operations on these objects, they appear in the corresponding classes, as routines (functions and procedures). Operations are not objects.
|
||||
|
||||
Sometimes, on the other hand, the "things" we model with our objects could represent operations. For example, we might want to build a list of tasks to be performed later; each task is defined by a routine. Each of the objects in the list will represent the corresponding routine.
|
||||
|
||||
Such an object, representing an operation, is called an agent.
|
||||
|
||||
If we can have a run-time object that represents an operation, then we can place the object in the structure of another object, where at some later time, a client can cause the associated operation to execute.
|
||||
|
||||
This is a very desirable model for event driven processing, like graphical user interfaces. The operations that are executed when a user take some action like clicking on a button, could be represented by agents. When the user interface element is initialized, agents that represent the action routines are stored within the interface element. Then at the time that an event, say a button click, occurs, the agents for that event are retrieved and their associated operations are executed.
|
||||
|
||||
Another area in which agents are commonly used is in traversing data structures. Many of the data structure classes in the Base Library include routines which take agents as there arguments. For example, the feature <code>do_all</code> takes an agent which represents some procedure and will apply the procedure to every item in the structure.
|
||||
|
||||
==Classes to Model Operations==
|
||||
|
||||
We know that there are two types of routines in Eiffel, functions and procedures.
|
||||
|
||||
The implementation of agents correspondingly relies on three classes in the Base Library: class <code>ROUTINE</code> for the general notion, and its heirs <code>FUNCTION</code>, with and <code>PROCEDURE</code>. In addition, <code>PREDICATE</code>, an heir of <code>FUNCTION</code> , covers the particular case of a function returning a boolean result.
|
||||
|
||||
When you use an agent from a client routine, you will be building an instance of either <code>FUNCTION</code> or <code>ROUTINE</code>.
|
||||
|
||||
==Using Agents==
|
||||
|
||||
Below is an instruction which passes an agent as an argument to a procedure.
|
||||
<code>
|
||||
button.select_actions.extend (agent gauge.step_forward)
|
||||
</code>
|
||||
|
||||
In this example, the producer wants to add the action of stepping the gauge forward in the event that a button is clicked. The keyword "<code>agent</code>" is used to indicate that at runtime an object of type <code>PROCEDURE</code> should be created which represents applying the feature <code>step_forward</code> to the object attached to <code>gauge</code>. It is the object of type <code>PROCEDURE</code> that is passed as the argument.
|
||||
|
||||
It is important to understand that <code>step_forward</code> does not get applied at the point that the instruction above is executed. Rather the procedure object that represents <code>step_forward</code> is given to the button to hold in reserve. Then at the point that the button click event takes place, the button will go through its list of <code>select_actions</code> executing their associated routines. Only then does <code>step_forward</code>get applied to <code>gauge</code>.
|
||||
|
||||
===Agents with Arguments===
|
||||
|
||||
In this example, the routine "<code>step_forward</code>" on which the agent is based takes no arguments. If you drilled down into the workings of this example you would find that class that implements the feature <code>extend</code> is class <code>EV_NOTIFY_ACTION_SEQUENCE</code>. You would also see that the signature for the feature <code>extend</code> is as essentially as follows.
|
||||
<code>
|
||||
extend (v: PROCEDURE [TUPLE])
|
||||
</code>
|
||||
|
||||
We don't have to know too much about the workings of agents to see that "<code>extend</code>" takes an argument <code>v</code> which is of type <code>PROCEDURE</code>. The actual generic parameter <code>TUPLE</code> represents the set of "open" arguments. In this case, <code>extend</code> is expecting an agent with no open arguments.
|
||||
|
||||
===Open and Closed Arguments===
|
||||
|
||||
It is this business of open and closed arguments which really makes agents remarkable. To get a feel for it, let's simplify the example some. Instead of considering an agent passed as an argument let's look at it as a simple assignment within a class.
|
||||
|
||||
Suppose a class has a feature declared as shown below.
|
||||
<code>
|
||||
my_procedure: PROCEDURE [TUPLE]
|
||||
</code>
|
||||
|
||||
Then what can be assigned to <code>my_procedure</code>?. An agent, of course. Say the class has procedures as follows.
|
||||
<code>
|
||||
no_argument_procedure
|
||||
-- A procedure with no arguments
|
||||
do
|
||||
print ("No argument here!%N")
|
||||
end
|
||||
|
||||
two_argument_procedure (an_int: INTEGER; another_int: INTEGER)
|
||||
-- A procedure with two arguments
|
||||
do
|
||||
print ("My arguments are: " + an_int.out + " and " + another_int.out + "%N")
|
||||
end
|
||||
</code>
|
||||
|
||||
Then the following assignment is valid.
|
||||
<code>
|
||||
my_procedure := agent no_argument_procedure
|
||||
</code>
|
||||
|
||||
What this means is that the agent created and associated with the procedure <code>no_argument_procedure</code> must conform to the type <code>PROCEDURE [TUPLE]</code>. The feature <code>my_procedure</code> (which is of type <code>PROCEDURE [TUPLE]</code>) can be attached at runtime to an agent representing a procedure with no open arguments, which indeed is what <code>no_argument_procedure</code> is.
|
||||
|
||||
Now let's turn our attention to the other procedure <code>two_argument_procedure</code>. You might think that because it takes two arguments, that you would not be able to build an agent from it which could be assigned to the attribute <code>my_procedure</code>. But you can do it by closing the two arguments at the time that the agent is created, as in the following.
|
||||
<code>
|
||||
my_procedure := agent two_argument_procedure (1, 2) -- Is Valid
|
||||
</code>
|
||||
|
||||
What happens here is that values are fixed for those arguments at the time that the agent, an object of type <code>PROCEDURE [ TUPLE]</code> is created.
|
||||
|
||||
So this is the wonderful thing about agents. A routine which will be represented as an agent does not have to be an exact fit for the expected signature. By closing some arguments at agent creation, you have effectively produced a new and conforming routine.
|
||||
|
||||
The advantage of this is that you can sometimes avoid building specialized routines for the sole purpose of having a routine which conforms to the agent signature.
|
||||
|
||||
To leave an argument open, you hold its place with a question mark. If you intend for all arguments to be open, then you may make them all question marks, or leave off the arguments entirely.
|
||||
<code>
|
||||
my_procedure := agent two_argument_procedure (?, 2) -- Argument 1 left open
|
||||
my_procedure := agent two_argument_procedure (?, ?) -- Both arguments left open
|
||||
my_procedure := agent two_argument_procedure -- Both arguments left open
|
||||
</code>
|
||||
|
||||
If an argument is open, then it means that a value is not provided for that argument at the time that the agent is created. The implication is that the value must be provided at some time prior to the time that the agent's associated routine gets executed. A precondition to executing a routine associated with an agent is that the agent has a valid set of arguments (called operands within the <code>ROUTINE</code> classes) for the call. If you were to leave one or both of the arguments to <code>two_argument_procedure</code> open as in the examples above, the assignment would still work due to the rules governing <code>TUPLE</code> conformance. But, at runtime unless the other arguments had been provided, the "<code>valid operands</code>" precondition would be violated.
|
||||
|
||||
Let's see an example in which we leave a target open. Suppose we have a class that has a feature coded as below
|
||||
<code>
|
||||
my_strings: LINKED_LIST [STRING]
|
||||
</code>
|
||||
|
||||
and some code to put some strings in <code>my_strings</code>:
|
||||
<code>
|
||||
create my_things.make
|
||||
my_strings.extend ("Hello")
|
||||
my_strings.extend ("World!")
|
||||
</code>
|
||||
|
||||
Our class also has a feature called <code>print_on_new_line</code> which we created to print a string preceded by a new line character.
|
||||
<code>
|
||||
print_on_new_line (s: STRING)
|
||||
-- Print `s' preceded by a new line
|
||||
do
|
||||
print ("%N" + s)
|
||||
end
|
||||
</code>
|
||||
|
||||
Now suppose we want to print the values of all the strings in <code>my_strings</code> each on a separate line by invoking <code>print_on_new_line</code>. Traditionally, we would do it by traversing the <code>LINKED_LIST</code> and printing each item. Like this:
|
||||
<code>
|
||||
from
|
||||
my_list.start
|
||||
until
|
||||
my_list.exhausted
|
||||
loop
|
||||
print_on_new_line (my_list.item)
|
||||
my_list.forth
|
||||
end
|
||||
</code>
|
||||
|
||||
The availability of agents gives us new options. <code>LINKED_LIST</code> has a feature <code>do_all</code> which comes to it from its ancestor <code>LINEAR</code>. The <code>do_all</code> feature's signature looks like this:
|
||||
<code>
|
||||
do_all (action: PROCEDURE [TUPLE [G]])
|
||||
</code>
|
||||
|
||||
As an argument <code>do_all</code> takes an agent based on a procedure with one open argument which is the same type as the list items (in this class, <code>G</code> is the formal generic parameter representing the type of the items being stored). Then it traverses the list executing the routine associated with that agent and uses the current list item to satisfy the open argument.
|
||||
|
||||
Instead of coding the loop shown above, we can code this instruction:
|
||||
<code>
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
</code>
|
||||
|
||||
we leave open the argument required by <code>print</code>, and <code>do_all</code> will provide it as a reference to the current list item as it traverses the list.
|
||||
|
||||
===Targets for Agents' Routines===
|
||||
|
||||
In Eiffel every routine must be applied against a target object. In our model for computation, <code>x.f (a, ...)</code>, the <code>x</code> is the target of the application of feature <code>f</code>. In the case of an agent, the agent must account for objects for each of the arguments and an object for the target of the routine.
|
||||
|
||||
Let's identify the targets in the examples shown. First:
|
||||
<code>
|
||||
button.select_actions.extend (agent gauge.step_forward)
|
||||
</code>
|
||||
|
||||
Here the target is the object attached to the entity "gauge" which is (although you cannot determine it from this line taken out of context) an object of type <code>EV_GAUGE</code>.
|
||||
|
||||
How about this:
|
||||
<code>
|
||||
my_procedure := agent two_argument_procedure (1, 2)
|
||||
</code>
|
||||
|
||||
Here, since there was no qualification, then the target is the current instance. Same with this:
|
||||
<code>
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
</code>
|
||||
|
||||
Again, consider the fact that the agent must account for objects for each of the arguments to a routine, and an object for the target. So, in the examples we've seen so far, the target is close, that is provided at the time of the creation of the agent.
|
||||
|
||||
But we can actually leave the target open as well. Now we cannot use the question mark notation to do that, because if we did, there would be no way to know of which class the routine is a feature. So instead, we mark an open target with the class name in braces.
|
||||
|
||||
Suppose in our list of strings example, we wanted to print the strings, then convert them to lower case, then print them again. Remember that "do_all" has one open argument, which will be provided as the current list item during the traversal.
|
||||
<code>
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
my_list.do_all (agent {STRING}.to_lower)
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
</code>
|
||||
|
||||
In between printing the list two times, we provide <code>do_all</code> with an agent that representing the <code>STRING</code> class's feature <code>to_lower</code> which will convert each string in the list to lower case. Notice that <code>to_lower</code> does not take an argument of type <code>STRING</code> as <code>print_on_new_line</code> did. Rather it gets applied to an instance of <code>STRING</code>, so it is targeted to a string. So we leave its target open and <code>do_all</code> provides the current list item as the target.
|
||||
|
||||
Agents for Functions
|
||||
|
||||
So far all the agents that we have coded have created instances of <code>PROCEDURE</code>. But functions are routines and can be represented as agents as well. The difference is that functions have a return value.
|
||||
|
||||
Let's extend the string example by using an agent that represents a function. Suppose we wanted to print only those strings which contain a particular character, say the exclamation point.
|
||||
|
||||
Here again we'll use a feature of the <code>LINKED_LIST</code> class. There is a feature called <code>do_if</code> which takes two agents as arguments. One is an action procedure like the argument that <code>do_all</code> takes, and the other is a function which returns a boolean and used as a test. As each list item is current, the test is applied first. If the result is true, then the action is applied with the current item.
|
||||
<code>
|
||||
my_list.do_if (agent print_on_new_line(?), agent {STRING}.has('!'))
|
||||
</code>
|
||||
|
||||
|
||||
The agent for the action is the same as we used earlier. We've added an agent for the test. It represents applying the <code>has</code> feature of the <code>STRING</code> class. Here the target is left open, because we want each of the strings in the list to be the target of <code>has</code>.
|
||||
|
||||
|
||||
Compatibility note
|
||||
|
||||
Versions of the Kernel Library classes ROUTINE, PROCEDURE, FUNCTION and PREDICATE prior to EiffelStudio 17-05 had an extra generic parameter at the initial position; the usual actual generic parameter was ANY. It has been removed. The compiler has been engineered so that in almost all cases it will still accept the old style.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
[[Property:title|Exception Mechanism]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|84a159dd-4a19-af73-7b0b-0618a284142a]]
|
||||
==Motivation: Concerning Robustness==
|
||||
|
||||
The notion of software correctness that we saw in [[Design by Contract and Assertions|Design by Contract and Assertions]] is half of the formula for software reliability. Correctness covers what the software is supposed to do, that is, its specification.
|
||||
|
||||
Of course,there is always a potential for things to go out of the bounds of the specification. This happens, for exampleif a client makes a call to a routine from a state in which the routine's precondition is not true.
|
||||
|
||||
How well software responds to situations which outside specification is called robustness. Together correctness and robustness define software reliability.
|
||||
|
||||
==Consequences of Contracts==
|
||||
|
||||
In the presence of Design by Contract, what happens to running software boils down to a simple rule:
|
||||
|
||||
A routine callcan complete in one of only two ways:
|
||||
# The routine fulfills its contract.
|
||||
# The routine fails to fulfill its contract.
|
||||
|
||||
As a follow-on, we can add that:
|
||||
* Any routine that fails to fulfill its contract must cause an exception in its caller.
|
||||
|
||||
==Reacting to Exceptions==
|
||||
|
||||
Again, because of Design by Contract, we can state the following rule for dealing with exceptions:
|
||||
|
||||
A routine that incurs an exeception can react in one of only two ways:
|
||||
# It can return the instance to a stable state and retry the entire routine with the same or a different strategy.
|
||||
# It can fail, causing an exception in its caller.
|
||||
|
||||
There is an Eiffel mechanism called the rescue clause which facilitates the first alternative.
|
||||
|
||||
===The Rescue Clause===
|
||||
|
||||
The <code>rescue</code> clause is part of the routine structure we saw in [[Adding Class Features|Adding Class Features]] . The rescue clause is a sequence of instructions introduced by the keyword "<code>rescue</code>". At the point that an exception occurs, the processing of the normal instructions in the routine body will cease, and the instructions in the rescue clause will be executed instead.
|
||||
|
||||
If the instructions in the rescue clause can set things up so that it might prove fruitful to attempt to retry the routine, it can do so by issuing a <code>retry</code> instruction. When the <code>retry</code> instruction is executed, the routine is restarted from its beginning, although local entities are not re-initialized. You will see why in the example below.
|
||||
|
||||
If the rescue clause exits without issuing a <code>retry</code> instruction, then the routine fails.
|
||||
|
||||
It should be noted that rescue clauses and retry instructions are not something that are used commonly. Out of the approximately 2000 classes in the delivered Eiffel libraries, there are only 16 occurrences. Many of these are oriented toward network and database operations for which some reasonable recovery might be possible.
|
||||
<code>
|
||||
transmit: (p: PACKET)
|
||||
-- Transmit packet `p'
|
||||
require
|
||||
packet_not_void: p /= Void
|
||||
local
|
||||
current_retries: INTEGER
|
||||
r: RANDOM_NUMBER_GENERATOR
|
||||
do
|
||||
line.send (p)
|
||||
rescue
|
||||
if current_retries < max_retries then
|
||||
r.next
|
||||
wait_millisecs (r.value_between (20, 500))
|
||||
current_retries := current_retries + 1
|
||||
retry
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
In the example above, <code>rescue</code> is used to recover from a situation in which an exception occurs in trying to send a packet. When the exception occurs the rescue clause will, if the maximum number of retries has not been reached, wait for some random length of time. Then, after having updated the number of retries, it will issue the <code>retry</code> instruction. If the maximum number of retries is reached, the rescue clause will exit without executing the retry, constituting a failure.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
[[Property:title|Genericity]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|3a0bfe10-78e7-00eb-d9a0-b977d1fa352a]]
|
||||
We got a very short introduction to generic classes when we were looking at the formal generic part of class structure in [[Eiffel Classes|Eiffel Classes]] . That discussion left to the imagination the motivation and benefits for creating generic classes.
|
||||
|
||||
You will see that most of the generic classes model containers for multiple items and at least one of their formal generic parameters represents the type of items that are to be stored in the container. Some generic classes, like <code>LINKABLE</code>, care for only one instance of the type represented by their formal generic parameter.
|
||||
|
||||
==Motivation==
|
||||
|
||||
Imagine that a software producer is planning to build a class which would represent a list of things. Someone might ask "What kinds of things?" To which the producer would reply, "Just things. I want my list to be usable for all kinds of things."
|
||||
|
||||
Using the idea of polymorphic attachment that we learned in [[Inheritance|Inheritance]] , the producer could build such a class. It might have a query <code>item</code> which would return the thing from the list to which a cursor currently points. It might have a command <code>put</code> which would enter some new thing into the list.
|
||||
|
||||
What would be the type of <code>item</code>? And what would be the type of the argument to <code>put</code>?
|
||||
|
||||
If the producer wants the class to handle all kinds of things, then the answer must be class <code>ANY</code>, the class from which all others inherit.
|
||||
<code>
|
||||
class
|
||||
LIST_OF_THINGS
|
||||
...
|
||||
feature -- Access
|
||||
|
||||
item: ANY
|
||||
-- The thing currently pointed to by cursor
|
||||
...
|
||||
feature -- Element change
|
||||
|
||||
put (new_item: ANY)
|
||||
-- Add `new_item' at the end of the list
|
||||
...
|
||||
</code>
|
||||
|
||||
This will work, but has some definite disadvantages. Suppose you choose to use this class to maintain a list of cats in one of your classes. You might make this declaration:
|
||||
<code>
|
||||
my_cats: LIST_OF_THINGS
|
||||
-- A list of my cats
|
||||
</code>
|
||||
|
||||
Then you could add individual instances to the list:
|
||||
<code>
|
||||
fluffy, twinkie: CAT
|
||||
...
|
||||
my_cats.put (fluffy)
|
||||
my_cats.put (twinkie)
|
||||
</code>
|
||||
|
||||
One problem with this type of list is that the type system will not help you keep from doing something pathological like:
|
||||
<code>
|
||||
fluffy, twinkie: CAT
|
||||
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
|
||||
-- A very nasty dog
|
||||
...
|
||||
my_cats.put (fluffy)
|
||||
my_cats.put (twinkie)
|
||||
my_cats.put (thor)
|
||||
</code>
|
||||
|
||||
Another problem is that to do any <code>CAT</code> things with an item in the list, you must reattach it to a <code>CAT</code> entity. The following is invalid.
|
||||
<code>
|
||||
my_cats.item.purr -- Is invalid
|
||||
</code>
|
||||
|
||||
This is because "item" is type <code>ANY</code> and although it may be currently attached to an instance of <code>CAT</code>, the static typing system cannot guarantee that. So you must use an object test as we saw in the polymorphism example in [[Inheritance|Inheritance]] .
|
||||
<code>
|
||||
...
|
||||
if attached my_cats.item as some_cat then
|
||||
some_cat.purr
|
||||
end
|
||||
</code>
|
||||
|
||||
You can see that this type of list has its drawbacks. Of course you could build a <code>LIST_OF_CATS</code> class in which <code>item</code> and the argument for <code>put</code> would be of type <code>CAT</code>. This would let you <code>purr</code> a cat without pulling it out of the list, and it would also prevent you from accidently letting old Thor in with the cats. But, every time you needed a list to hold a different type of object, you have to write a new class.
|
||||
|
||||
Indeed, this is how things are done in environments without facilities genericity.
|
||||
|
||||
What we would like to have is a way to produce the text of the list class once. Then only when we make declarations do we add the additional information about the particular types we want allowed in the list.
|
||||
|
||||
==Basic Genericity==
|
||||
|
||||
In Eiffel this is accomplished through generic classes. Generic classes are written relative not to a specific class but to a kind of phony class name called a formal generic parameter. With genericity, the <code>LIST_OF_THINGS</code> class might become a class called <code>LIST</code> which is a list of items of type <code>G</code>. In class <code>LIST</code> we would declare <code>item</code> as type G, as well as the argument to <code>put</code>.
|
||||
<code>
|
||||
class
|
||||
LIST [G]
|
||||
...
|
||||
feature -- Access
|
||||
item: G
|
||||
-- The item currently pointed to by cursor
|
||||
...
|
||||
feature -- Element change
|
||||
put (new_item: G)
|
||||
-- Add `new_item' at the end of the list
|
||||
...
|
||||
</code>
|
||||
|
||||
We could declare feature <code>my_cats</code> as a <code>LIST</code> of items of type <code>CAT</code>. By doing so we are providing <code>CAT</code> as an "actual generic parameter" in the declaration. Then we are free to treat the features of <code>LIST</code> as if the class name <code>CAT</code> had been substituted for every occurrence of the formal generic parameter <code>G</code>.
|
||||
<code>
|
||||
my_cats: LIST [CAT]
|
||||
-- A list of my cats
|
||||
fluffy, twinkie: CAT
|
||||
...
|
||||
my_cats.put (fluffy)
|
||||
my_cats.put (twinkie)
|
||||
...
|
||||
my_cats.item.purr -- Valid now
|
||||
</code>
|
||||
|
||||
The following would no longer be valid:
|
||||
<code>
|
||||
my_cats: LIST [CAT]
|
||||
-- A list of my cats
|
||||
|
||||
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
|
||||
...
|
||||
my_cats.put (thor) -- Is invalid
|
||||
</code>
|
||||
|
||||
==Constrained Genericity==
|
||||
|
||||
The generic class <code>LIST</code> illustrated above is perfectly useful for making typed lists of any type of object. The features of the <code>LIST</code> will not attempt to use the objects in the list in any way. Sometimes though, it is important for a class to be guaranteed more about the nature of the types that can be substituted for its formal generic parameter.
|
||||
|
||||
Take for example the case of a class called <code>SORTED_LIST</code>. A <code>SORTED_LIST</code> is a list, of course, but it is special in that it acts upon the elements that it holds to keep them in order.
|
||||
|
||||
A <code>SORTED_LIST</code> needs to be able to order its elements. So, it must be able to apply queries to those elements to determine which should sort high than which. The elements themselves must respond to ordering operations.
|
||||
|
||||
If <code>SORTED_LIST</code> were defined like we did <code>LIST</code>
|
||||
<code>
|
||||
class
|
||||
SORTED_LIST [G]
|
||||
</code>
|
||||
|
||||
there would be no guarantee that ordering operations, like "<" and ">" could be applied to all types that could be listed. An Eiffel facility called "constrained genericity" will solve this problemfor us. In the case of <code>SORTED_LIST</code>, we would add to the formal generic part as follows.
|
||||
<code>
|
||||
class
|
||||
SORTED_LIST [G -> COMPARABLE]
|
||||
</code>
|
||||
|
||||
You may remember from [[Inheritance|Inheritance]] that if we make instances of a class comparable with each other, then we make the class inherit from <code>COMPARABLE</code> and effect the feature "<".
|
||||
|
||||
Here, constrained genericity does two things for us.
|
||||
* First, it states that any candidate for substitution for <code>G</code> must conform to class <code>COMPARABLE</code>. Typically this means it must inherit from <code>COMPARABLE</code>.
|
||||
* Second, it allows, within the features of <code>SORTED_LIST</code>, the features of <code>COMPARABLE</code> to be applied to any item which has a type of <code>G</code>.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[[Property:title|Eiffel for .NET]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|446038d8-abd6-0a0d-2b90-94124e1ac810]]
|
||||
These pages contain documentation that describes the Eiffel programming language.
|
||||
|
||||
Eiffel is considered both a development methodology and a programming language.
|
||||
|
||||
Eiffel the methodology is a recipe for constructing quality software which is based on a small number of powerful concepts. Eiffel the programming language is a notation which is designed to support the methodology.
|
||||
|
||||
Eiffel for.NET is the Eiffel programming language made available in the .NET environment. Eiffel for.NET components can use and inherit from components in .NET assemblies that were produced using other.NET languages. And, programmers using other.NET languages can use and inherit from components in assemblies produced using Eiffel for.NET.
|
||||
|
||||
==Eiffel for.NET Conventions Documentation==
|
||||
|
||||
In Eiffel, as in other languages, certain conventions have evolved which constitute "the way things are done". Conventions provide consistency and therefore predictability and readability of software text.
|
||||
|
||||
When working in the.NET components, particularly when using components from .NET assemblies delivered from Microsoft or produced by other parties, the conventions of Eiffel are maintained in the view of these components. The first section, titled [[Conventions]], provides you with an understanding of Eiffel terminology, conventions , and how they work with .NET.
|
||||
|
||||
The remaining sections of this section are a reference for the Eiffel for .NET language. It is not a complete reference, though. Rather, it is intended to help you with the tasks that Eiffel programmers commonly encounter.
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
[[Property:modification_date|Sat, 12 Jan 2019 18:39:06 GMT]]
|
||||
[[Property:publication_date|Sat, 12 Jan 2019 18:34:23 GMT]]
|
||||
[[Property:title|Inheritance]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|7e4cb7ba-fda6-8eac-3e27-bbb8fafd8673]]
|
||||
Inheritance, along with client/supplier, are the two relationships that can exist between classes.
|
||||
|
||||
Inheritance lets us mirror in software the types of abstractions that are common in many problem domains, i.e., the more general to the more specialized.
|
||||
|
||||
Inheritance also gives a way us to combine these abstractions.
|
||||
|
||||
Inheritance allows us to make extensions and adaptations to existing software, while at the same time, leaving the original software unaltered.
|
||||
|
||||
==The Eiffel Inheritance Model==
|
||||
|
||||
If class <code>B</code> inherits from class <code>A</code>, then:
|
||||
* Every feature of <code>A</code> is also a feature of <code>B</code>
|
||||
* In any case in which an instance of <code>A</code> is called for, then an instance of <code>B</code> will suffice.
|
||||
|
||||
Flexibility and adaptability are key qualities of the Eiffel inheritance model. On an informal level, this means that, except as prevented by certain constraints, a class can inherit from a set of classes containing just about any other classes.
|
||||
|
||||
Eiffel classes can be effective or deferred. If a class is effective, then it is completely implemented. As a result, it is possible to create and use direct instances of an effective class at runtime.
|
||||
|
||||
If a class is deferred, then it is not completely implemented. A class is deferred if it contains at least one deferred feature. So, it is possible for you to mark a feature (and by consequence also its class) as deferred when you code it. This means that the specification for this class dictates that such a feature exists, but there is no implementation for the feature included in the class. As a result, there can be no direct instances of deferred classes at runtime. However, a class that inherits from a deferred class can implement, or effect, the deferred features. This results in an effective descendant to the deferred class. And it is possible to create direct instances of this effective descendant. Such instances would also be instances (albeit not direct instances) of the original deferred class.
|
||||
|
||||
What this means to us as software producers, is that in any development effort, we have available a great number of classes which can serve as potential starting points. That is, classes that we could make parents to the classes we produce. And, those classes do not have to chosen from a strict dichotomy of classes which are either completely abstract or completely implemented. Inheritance from classes that are deferred but have some implemented features is both possible and encouraged. It reuses existing software and it reduces the opportunity for error.
|
||||
|
||||
Consider the deferred class <code>COMPARABLE</code> from the Eiffel Base Library. A portion of <code>COMPARABLE</code> is shown below:
|
||||
<code>
|
||||
deferred class
|
||||
COMPARABLE
|
||||
|
||||
feature -- Comparison
|
||||
|
||||
is_less alias "<" (other: like Current): BOOLEAN
|
||||
-- Is current object less than `other'?
|
||||
deferred
|
||||
end
|
||||
|
||||
is_less_equal alias "<=" (other: like Current): BOOLEAN
|
||||
-- Is current object less than or equal to `other'?
|
||||
do
|
||||
Result := not (other < Current)
|
||||
end
|
||||
|
||||
is_greater alias ">" (other: like Current): BOOLEAN
|
||||
-- Is current object greater than `other'?
|
||||
do
|
||||
Result := other < Current
|
||||
end
|
||||
|
||||
is_greater_equal alias ">=" (other: like Current): BOOLEAN
|
||||
-- Is current object greater than or equal to `other'?
|
||||
do
|
||||
Result := not (Current < other)
|
||||
end
|
||||
|
||||
is_equal (other: like Current): BOOLEAN
|
||||
-- Is `other' attached to an object of the same type
|
||||
-- as current object and identical to it?
|
||||
do
|
||||
Result := (not (Current < other) and not (other < Current))
|
||||
end
|
||||
</code>
|
||||
|
||||
If you are producing a class that you wish to support basic comparison operators, like "<" and ">", you can have that class inherit from <code>COMPARABLE</code>, which has features which correspond to those operators. The text for <code>COMPARABLE</code> contains eight features. Seven of these are effective and one is deferred.
|
||||
|
||||
So through inheritance from <code>COMPARABLE</code>, your class, let's call it <code>WHATZIT</code>, would now have these features available. But how would the features of <code>COMPARABLE</code> know what it means to compare <code>WHATZIT</code>s?
|
||||
|
||||
Of course, it would have no way of knowing, so you must show it. And you do that by writing the implementation for "<", the one deferred feature that <code>WHATZIT</code> inherits from the <code>COMPARABLE</code> class.
|
||||
|
||||
When you look closely at the effective features of <code>COMPARABLE</code>, you see that their implementations are ultimately based on "<". If we were not able to inherit from multiple partially implemented classes, then we would be forced to implement many more features, a process which invites error, or, in the case of comparison, to move to a less appealing model.
|
||||
|
||||
==The Inheritance Part of Classes in Eiffel==
|
||||
|
||||
Because the inheritance model has such flexibility, it must also have adaptability. A consequence of inheriting from multiple classes is that it would be possible to inherit multiple features with the same name ... and you remember from [[Adding Class Features|Adding Class Features]] that a class is not allowed to have more than one feature with the same name. A process called feature adaptation allows us to resolve these issues in an heir. Feature adaptation is also done for reasons other than resolving name clashes as well.
|
||||
|
||||
Feature adaptation is an enabling capability, but it is also one that takes some study to understand fully.
|
||||
|
||||
We will look at the types of feature adaptation that will serve most useful to you as you begin to produce Eiffel software.
|
||||
|
||||
In [[Eiffel Classes|Eiffel Classes]] you saw where the inheritance part fits into the class structure. Shown below is a portion of class <code>LINKED_QUEUE</code> from the Eiffel libraries. <code>LINKED_QUEUE</code> is an effective class which implements the abstract notion of a <code>QUEUE</code> (a deferred class) with an implementation based on the services provided by <code>LINKED_LIST</code> (an effective class).
|
||||
<code>
|
||||
class
|
||||
LINKED_QUEUE [G]
|
||||
inherit
|
||||
QUEUE [G]
|
||||
undefine
|
||||
is_empty,
|
||||
copy,
|
||||
is_equal
|
||||
redefine
|
||||
linear_representation,
|
||||
prune_all,
|
||||
extend
|
||||
select
|
||||
item,
|
||||
put
|
||||
end
|
||||
LINKED_LIST [G]
|
||||
rename
|
||||
item as ll_item,
|
||||
remove as ll_remove,
|
||||
make as ll_make,
|
||||
remove_left as remove,
|
||||
put as ll_put
|
||||
export
|
||||
{NONE}
|
||||
all
|
||||
{ANY}
|
||||
writable,
|
||||
extendible,
|
||||
wipe_out,
|
||||
readable
|
||||
undefine
|
||||
fill,
|
||||
append,
|
||||
prune,
|
||||
readable,
|
||||
writable,
|
||||
prune_all,
|
||||
extend,
|
||||
force,
|
||||
is_inserted
|
||||
redefine
|
||||
duplicate,
|
||||
linear_representation
|
||||
select
|
||||
remove
|
||||
end
|
||||
</code>
|
||||
|
||||
Okay ... now calm down ... please. This is an example from a very highly-evolved and sophisticated library which is replete with software reuse. <code>LINKED_QUEUE</code> has two parents and uses considerable feature adaptation. In fact, it uses every feature adaptation option available. The benefit is obvious, though. <code>LINKED_QUEUE</code> class has only seven features actually coded. In total there are only 26 lines of instructions!
|
||||
|
||||
In practice you can use inheritance, even multiple inheritance, to do some quite productive programming in Eiffel without having to write anything that looks like the inheritance part of <code>LINKED_QUEUE</code> above.
|
||||
|
||||
Regardless, let's break <code>LINKED_QUEUE</code>'s inheritance part into chunks and take a look at some of them.
|
||||
|
||||
===Rename===
|
||||
<code>
|
||||
rename
|
||||
item as ll_item,
|
||||
remove as ll_remove,
|
||||
make as ll_make,
|
||||
remove_left as remove,
|
||||
put as ll_put
|
||||
</code>
|
||||
|
||||
As you might have already guessed, the rename part, introduced oddly enough by the keyword "<code>rename</code>", is used to rename features.
|
||||
|
||||
Specifically, it is used when an heir wants to use a feature from a parent, but wants to use it under a different name than that by which the parent knows it. So in the example, the feature known as <code>item</code> in <code>LINKED_LIST</code> is perfectly usable in <code>LINKED_QUEUE</code>, but must be applied as <code>ll_item</code>.
|
||||
|
||||
This is common when your class inherits two different features with the same name from two different parents and you want to be able to use them both. Because you can only have one feature with a given name, then rename one of the features.
|
||||
|
||||
===New Exports===
|
||||
<code>
|
||||
export
|
||||
{NONE}
|
||||
all
|
||||
{ANY}
|
||||
writable,
|
||||
extendible,
|
||||
wipe_out,
|
||||
readable
|
||||
</code>
|
||||
|
||||
The new exports part is introduced by the keyword "<code>export</code>". This section allows you to change the export status of inherited features. Remember from [[Adding Class Features|Adding Class Features]] that features become available (or not) to clients by their export status. Export status of immediate features is controlled in the feature clause. But here we are dealing with inherited features, so we control their status in the export part of the class's inheritance section. Any feature not mentioned will have the same export status as it did in the parent class.
|
||||
|
||||
In this example, the keyword "<code>all</code>" is used first to say that all features inherited form <code>LINKED_LIST</code> are unavailable to any clients (export to class <code>NONE</code>). This is typical for a class like <code>LINKED_QUEUE</code> in which the features important to the client come from the deferred parent, in this case <code>QUEUE</code>, and the class <code>LINKED_LIST</code> is used only for implementation. But, it seems that also in this case, the producer felt differently about the features <code>writable</code>, <code>extendible</code>, <code>wipe_out</code>, and <code>readable</code>, and decided the allow clients of <code>ANY</code> type to utilize these features inherited from <code>LINKED_LIST</code>.
|
||||
|
||||
===Undefine===
|
||||
<code>
|
||||
undefine
|
||||
is_empty,
|
||||
copy,
|
||||
is_equal
|
||||
</code>
|
||||
|
||||
Next, undefine ... it's probably not what you think. You might assume that undefine is a way to banish forever any inherited features that you just do not want to deal with. But what happens to features whose names are listed in an undefine clause is that they become deferred features in the heir.
|
||||
|
||||
Undefine is useful if you inherit two different features of the same name from different parents, a situation you cannot live with. If you like one and you don't like the other, then you can undefine the one you don't like. The the only version you get is the one you like.
|
||||
|
||||
Another way you might use undefine is in the case in which you actually want a feature to be deferred in an heir that was effective in a parent.
|
||||
|
||||
===Redefine===
|
||||
<code>
|
||||
redefine
|
||||
linear_representation,
|
||||
prune_all,
|
||||
extend
|
||||
</code>
|
||||
|
||||
The redefine part lists the names of effective features for which the producer of the heir class would like to provide implementations that replace the inherited implementations.
|
||||
|
||||
So, in this example the implementation for <code>linear_representation</code>, for example, that <code>LINKED_QUEUE</code> would have inherited from <code>QUEUE</code> will not be used. Instead <code>LINKED_QUEUE</code> implements its own version of <code>linear_representation</code>.
|
||||
|
||||
{{note|When a class implements a version of an inherited feature which was deferred in its parent, this is known as "effecting" the feature. Because features being effected are getting their first implementation, it is not necessary to list their names in the redefine part, or anywhere else in the inheritance part of the heir. }}
|
||||
|
||||
===Select===
|
||||
<code>
|
||||
select
|
||||
remove
|
||||
</code>
|
||||
|
||||
The select part is used only under special circumstances. The case in which select is required involves a situation called "repeated" inheritance. Repeated inheritance occurs when an heir inherits more than once from the same ancestor. Usually this means it has two or more parents who have a common proper ancestor (but it can occur directly). The features from the common ancestor are inherited by each of the parents and passed on to the heir. The rules and effects of repeated inheritance occupy an entire chapter in the official Eiffel programming language reference and will not be reproduced here. Just understand at this point that it is sometimes necessary to use <code>select</code> to provide the dynamic binding system with an unambiguous choice of features in the presence of polymorphic attachment.
|
||||
|
||||
You should note also that repeated inheritance can and does occur often without causing any problem at all. In fact it happens in every case of multiple inheritance, due to the fact that all classes inherit from class ANY and receive its features as a result. The reason it is not a problem is that in the case that any feature makes it from the original common ancestor along multiple paths to the heir with its name and implementation still intact, it will arrive as only one feature heir. This is called sharing and nothing special needs to be done to make it happen.
|
||||
|
||||
==Polymorphism==
|
||||
|
||||
It is time now to see another way in which inheritance helps build more extendible software.
|
||||
|
||||
Assume that we have to build classes that model different types of polygons. We would do this by building a class for polygon which would model a garden-variety polygon, a multi-sided closed figure. But when we consider that there are specialized types of polygons, like triangles and rectangles, we realize that to support these specializations, we need classes for them as well. And this is an obvious opportunity for inheritance. All triangles and rectangles are polygons. So, we start with class <code>POLYGON</code> and its proper descendants <code>TRIANGLE</code> and <code>RECTANGLE</code>.
|
||||
|
||||
So we can make declarations like:
|
||||
<code>
|
||||
my_polygon: POLYGON
|
||||
your_polygon: POLYGON
|
||||
my_triangle: TRIANGLE
|
||||
my_rectangle: RECTANGLE
|
||||
another_rectangle: RECTANGLE
|
||||
</code>
|
||||
|
||||
Assume these declarations are in force for all the examples this section on polymorphism.
|
||||
|
||||
We saw in [[Adding Class Features|Adding Class Features]] that we can say that one class conforms to another if it is the same class or one of its proper descendants. Therefore POLYGON conforms to <code>POLYGON</code>. Also, <code>TRIANGLE</code> and <code>RECTANGLE</code> conform to <code>POLYGON</code>. But, importantly, <code>POLYGON</code> does not conform to <code>TRIANGLE</code> or <code>RECTANGLE</code>. This makes sense intuitively, because we know all rectangles and triangles are polygons ... and we also know that not all polygons are rectangles.
|
||||
|
||||
===Polymorphic Attachment===
|
||||
|
||||
These facts affect how assignments can work. Using the declarations above:
|
||||
<code>
|
||||
my_polygon := your_polygon -- Is valid
|
||||
your_polygon :=my_polygon -- Is valid
|
||||
my_polygon :=my_rectangle -- Is valid
|
||||
my_polygon := my_triangle -- Is valid
|
||||
</code>
|
||||
|
||||
but
|
||||
<code>
|
||||
my_rectangle := my_polygon -- Is not valid
|
||||
my_triangle := my_polygon -- Is not valid
|
||||
</code>
|
||||
|
||||
and of course
|
||||
<code>
|
||||
my_rectangle := my_triangle -- Is not valid
|
||||
</code>
|
||||
|
||||
Consider now the assignment below which is valid.
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
</code>
|
||||
|
||||
After an assignment like this executes the entity <code>my_polygon</code> will be holding at runtime a reference to an instance of a type which is not a direct instance of its declared type <code>POLYGON</code>. But conformance ensures us that, although it may not be a direct instance, it will indeed by an instance. (all rectangles are polygons).
|
||||
|
||||
Depending upon how many different types of polygons get modeled in classes, the entity "<code>my_polygon</code>" could be attached objects of may different types ... it could take on many forms. This in fact is the basis for the term "polymorphism"; having many forms. So we speak of "polymorphic attachment" as the process by which at runtime entities can hold references to objects which are not of the entity's declared type ... but they are of conforming types.
|
||||
|
||||
Now let's see how we get some value from this.
|
||||
|
||||
===Dynamic Binding===
|
||||
|
||||
Suppose that one of the features of <code>POLYGON</code> is a query <code>perimeter</code> which returns an instance's perimeter. The producer of <code>POLYGON</code> may have implemented <code>perimeter</code> as a function that computes the perimeter by adding up the lengths of all the sides. This approach is guaranteed to work for all polygons, and we can apply the <code>perimeter</code> feature to any polygon. Let's print some perimeters:
|
||||
<code>
|
||||
print (my_polygon.perimeter)
|
||||
print (my_triangle.perimeter)
|
||||
print (my_rectangle.perimeter)
|
||||
</code>
|
||||
|
||||
<code>TRIANGLE</code> and <code>RECTANGLE</code> might have properties, expressed as queries, which as a part of their specialization, distinguish them from run-of-the-mill polygons. Two features of rectangles are <code>width</code> and <code>height</code> the lengths of the sides.
|
||||
|
||||
Armed with these <code>RECTANGLE</code>-specific features, the producer of <code>RECTANGLE</code> may say, "Now I no longer have to depend upon that crude implementation of <code>perimeter</code> that is inherited from <code>POLYGON</code>. I can build an efficient <code>RECTANGLE</code>-specific implementation of <code>perimeter</code>, based on the knowledge that for all <code>RECTANGLE</code>s perimeter = 2*(width+height)"
|
||||
|
||||
To implement this specialized version of <code>perimeter</code>, the producer of <code>RECTANGLE</code> must add the feature to the class, but also must list its name in the "<code>redefine</code>" part of the <code>RECTANGLE</code>'s inheritance clause.
|
||||
<code>
|
||||
class
|
||||
RECTANGLE
|
||||
inherit
|
||||
POLYGON
|
||||
redefine
|
||||
perimeter
|
||||
end
|
||||
.
|
||||
.
|
||||
feature
|
||||
perimeter: REAL
|
||||
-- Sum of lengths of all sides
|
||||
do
|
||||
Result := 2 * (width + height)
|
||||
end
|
||||
</code>
|
||||
|
||||
You would expect then, that this version of perimeter would be executed in the following context:
|
||||
<code>
|
||||
print (my_rectangle.perimeter)
|
||||
</code>
|
||||
|
||||
But what makes this interesting is that even in the context below
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
print (my_polygon.perimeter)
|
||||
</code>
|
||||
|
||||
in which <code>perimeter</code> is being applied to a entity declared as <code>POLYGON</code>, the specialized version of <code>perimeter</code> from <code>RECTANGLE</code> is being used. It would be impossible to ensure at compile time which version of <code>perimeter</code> is most appropriate. So it must be done at runtime. This ability to choose the best version of a feature to apply, just at the moment it needs to be applied, is called "dynamic binding".
|
||||
|
||||
Static typing tells us at compile time that it is safe to apply <code>perimeter</code> to <code>my_polygon</code> No matter which of the types of polygons is attached to <code>my_polygon</code>, there will be a <code>perimeter</code> feature that will work.
|
||||
|
||||
Dynamic binding tells us that when we apply <code>perimeter</code>, we know that the most appropriate version of the feature will get applied at runtime.
|
||||
|
||||
===Object Test===
|
||||
|
||||
Now let's add another situation. Consider the code below:
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
print (my_polygon.perimeter)
|
||||
print (my_polygon.width) -- Is invalid
|
||||
</code>
|
||||
|
||||
We could apply <code>perimeter</code> to <code>my_polygon</code> and everything is fine ... we even get <code>RECTANGLE</code>'s specialized version of the feature. But it is invalid for us to try to apply <code>width</code> to <code>my_polygon</code> even though we feel (with rather strong conviction) that at this point in execution, <code>my_polygon</code> will be attached to an object of type <code>RECTANGLE</code>, and we know that <code>width</code> is a valid query on <code>RECTANGLE</code>s.
|
||||
|
||||
The reason follows. When we declared <code>my_polygon</code> as type <code>POLYGON</code>, we made a deal that says that the only features that can be applied to <code>my_polygon</code> are the features of <code>POLYGON</code>. Remember that static typing guarantees us at compile time that at runtime there will be at least one version of the feature available that can be applied.
|
||||
<code>
|
||||
print (my_polygon.width) -- Is invalid
|
||||
</code>
|
||||
|
||||
But in the case above, the guarantee cannot be made. <code>my_polygon</code> is declared with class <code>POLYGON</code> which has no <code>width</code> feature, despite the fact that some of its proper descendants might.
|
||||
|
||||
Does this mean that we can never do <code>RECTANGLE</code> things with this instance again, once we have attached it to <code>my_polygon</code>?
|
||||
|
||||
No. There is a language facility called the '''object test''' which will come to our rescue. The object test will allow us safely to attach our instance back to an entity typed as <code>RECTANGLE</code>. After doing so, we are free use <code>RECTANGLE</code> features.
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
print (my_polygon.perimeter)
|
||||
if attached {RECTANGLE} my_polygon as l_rect then
|
||||
print (l_rect.width)
|
||||
end
|
||||
</code>
|
||||
In this code, the entity <code>l_rect</code> is a fresh local entity produced during the object test. So, the code can be read: if at this point, <code>my_polygon</code> is attached to an instance of type <code>RECTANGLE</code>, then attach that instance to a fresh local entity named <code>l_rect</code>, then apply <code>width</code> to <code>l_rect</code> and print the result.
|
||||
|
||||
|
||||
:'''Note:''' The object test replaces the functionality of an obsolete mechanism called assignment attempt. Assignment attempt used the syntax '''<code>?=</code>''' in the context of assignment versus the '''<code>:=</code>''' of normal assignment.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[[Property:title|Referenced Assembly Type and Feature Name Conversion]]
|
||||
[[Property:weight|10]]
|
||||
[[Property:uuid|5d575090-35d9-f983-7308-172b1641173f]]
|
||||
The [[The Eiffel for .NET language|Eiffel for .NET language]] is Eiffel. It is not a variant of Eiffel that spawned a new language, as is '''C#''', or a dramatic evolution of Eiffel, such as '''Visual Basic .NET''' is to '''VB6'''. As Eiffel stands today, and will probably remain, Eiffel's conventions for class names and features names match neither that of the OO Pascal variants nor the "Camel-casing" conventions recommended by Microsoft. Eiffel also does not support the notion of full quantified type names. The period (''''.'''') between namespace and type names is not valid Eiffel syntax. These naming convention rules pose a problem for maintaining the Eiffel programming language. To address this issue, when referencing .NET assemblies, all referenced assembly type names and feature names are converted into "Eiffel-case" when using Eiffel.
|
||||
<span id="eiffel_case"></span>
|
||||
==What is Eiffel-Case?==
|
||||
|
||||
Eiffel-casing is almost the same for both class and class feature names, all words are separated using underscores (''''_''''). The differences between them are class names are always in uppercase and feature names always in lowercase.
|
||||
|
||||
<eiffel>MY_CLASS</eiffel> is an example of an Eiffel-cased class name. <br/>
|
||||
<eiffel>my_feature</eiffel> is an example of an Eiffel-cased feature name.
|
||||
|
||||
There are somewhat complex rules to ensure that the automatic formatting of .NET name, type, method, property and otherwise are formatted to the most readable name possible, given their casing convention. One absolute is that the Eiffel compilation process will remove the namespace from the fully qualified type name and format just the type name.
|
||||
|
||||
For example <eiffel>System.Windows.Forms.Border3DStyle</eiffel> would yield <eiffel>BORDER_3D_STYLE</eiffel>.
|
||||
|
||||
Stripping the namespace from the name is essential to keep type names short and usable, who wants to use <eiffel>SYSTEM_WINDOWS_FORMS_BORDER_3D_STYLE</eiffel>? Dropping the namespace does present a potential problem; assemblies may have two type that are of the same name but are located in different namespaces. A resolution to this comes in the form of assembly prefixes. For every assembly a class name prefix can be specified. Eiffel Software have reserved a set of assembly prefix pairs which should not be change as they are used by the Eiffel class libraries. For example, ''System.Windows.Forms.dll'' contains a commonly used type called <eiffel>System.Windows.Forms.Form</eiffel>. During the compilation process ''System.Windows.Forms.dll'' will be evaluated and <eiffel>System.Windows.Forms.Form</eiffel> will yield the <eiffel>FORM</eiffel> Eiffel class name. ''System.Windows.Forms.dll'' has been assigned the '<eiffel>WINFORMS_</eiffel>' prefix, so <eiffel>FORM</eiffel> would actually be <eiffel>WINFORMS_FORM</eiffel> instead.
|
||||
<span id="prefixes"></span>
|
||||
==Reserved Prefixes==
|
||||
|
||||
The following table displays the fixed assembly class name prefixes, assigned by Eiffel Software, for the Microsoft Foundation Class Libraries:
|
||||
{|
|
||||
|-
|
||||
| mscorlib.dll
|
||||
| No Prefix
|
||||
|-
|
||||
| System.dll
|
||||
| <eiffel>SYSTEM_DLL_</eiffel>
|
||||
|-
|
||||
| System.XML.dll
|
||||
| <eiffel>XML_</eiffel>
|
||||
|-
|
||||
| System.Data.dll
|
||||
| <eiffel>DATA_</eiffel>
|
||||
|-
|
||||
| System.Web.dll
|
||||
| <eiffel>WEB_</eiffel>
|
||||
|-
|
||||
| System.EnterpriseServices.dll
|
||||
| <eiffel>ENTERPRISE_</eiffel>
|
||||
|-
|
||||
| System.Windows.Forms.dll
|
||||
| <eiffel>WINFORMS_</eiffel>
|
||||
|}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[[Property:title|Using Referenced Assemblies]]
|
||||
[[Property:weight|8]]
|
||||
[[Property:uuid|acbe0407-b95e-6268-fc20-238f91df595e]]
|
||||
Eiffel for.NET is a first class citizen in the Microsoft.NET programming world. This means that if you are programming in Eiffel for.NET, you have full access to the thousands of software components in the .NET type libraries. But, that's not all. You also have full access to the thousands of components in the traditional Eiffel class libraries. And even that's not all. You have the ability to build software components which comply with.NET standards, so that they can be used by programmers using any other .NET language. Still not all. When you use Eiffel, you can choose to build software that will run under Microsoft.NET, but will be portable to other popular operating systems as well.
|
||||
|
||||
Being an Eiffel for.NET programmer obviously put you in a very powerful position. How do you take advantage of it?
|
||||
|
||||
To use.NET software components from Eiffel for.NET requires you to have some understanding of both.NET and Eiffel and how their respective object models differ. If you have read the rest of the help topics in [[Eiffel for .NET|this section]] then you have a pretty good idea of what the Eiffel method and language are all about.
|
||||
|
||||
When you begin to build software in Eiffel for.NET, you will likely find a need to reuse types from the.NET libraries. These libraries are called assemblies. When you reference a assembly using Eiffel for.NET, the types in the assembly become available to you in a form that makes them look like so many Eiffel classes. The names for types and members will conform to Eiffel conventions. The same thing happens when you are programming against assemblies in Visual Basic.NET or Visual C#.NET.
|
||||
|
||||
The section called [[Conventions]] covers the details the Eiffel conventions and how the.NET types are made available to Eiffel for.NET programmers.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[[Property:modification_date|Mon, 02 Oct 2023 14:01:34 GMT]]
|
||||
[[Property:publication_date|Mon, 02 Oct 2023 14:01:34 GMT]]
|
||||
[[Property:title|The Eiffel for .NET language]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|ba6cd8d3-683c-4167-bdef-a0274c392f34]]
|
||||
This section focuses on defining Eiffel for .NET. The key requirement for Eiffel for .NET is the exclusive use of the common language runtime with a minimum Eiffel-specific runtime. The second requirement is to generate IL code that is CLS compliant, meaning that other CLS compliant languages/compilers/tools will be able to reuse .NET components written in Eiffel for .NET. The last requirement is to generate verifiable IL code.
|
||||
|
||||
Because not all Eiffel functionalities are present in .NET, the task of the Eiffel compiler is made more complicated since it has to emulate those mechanisms instead of reusing what .NET provides. For example the common language runtime of .NET does not support:
|
||||
* [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Multiple inheritance|multiple inheritance]]
|
||||
* [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Genericity|genericity]] (only first versions of .NET)
|
||||
* [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Covariance|covariance]]
|
||||
* agents
|
||||
|
||||
We will see:
|
||||
* what is Eiffel on .NET
|
||||
* which Eiffel mechanisms have been implemented and which haven't.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[[Property:title|Known issues]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|78c6b5c1-a87b-805d-38bd-fe64cb4e7c0d]]
|
||||
==Eiffel issues==
|
||||
* Check here first for the [[Eiffel for .NET Integration|details of the integration of Eiffel with .NET]] .
|
||||
* The root creation routine of the root class must not take any arguments. You can access the command line given to the application via the class <code>ARGUMENTS</code>.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user