diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/constructors-and-creation-procedures.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/constructors-and-creation-procedures.wiki index 3f02a7af..3f85e9d4 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/constructors-and-creation-procedures.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/constructors-and-creation-procedures.wiki @@ -7,13 +7,13 @@ This section deals with what happens when objects, that is runtime instances of 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 " make "). What makes them creation procedures is the fact that their names are listed as creation procedures in the class text. +There is nothing special about creation procedures themselves, they are just ordinary procedures (although by convention their names usually begin with the word "make"). 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 New . Once compiled into an assembly, the metadata labels constructors as . ctor . +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 New. Once compiled into an assembly, the metadata labels constructors as . ctor. Constructors can have multiple versions by overloading. That is, each version would have a different set of argument types. @@ -21,17 +21,17 @@ 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 " make ", the convention for creation procedure naming in Eiffel. +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 "make", 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 make . However, if there are overloaded versions of the constructor, then these versions given names starting with " make_from_ " and then followed with the argument names from the assembly metadata separated with the conjunction " _and_ ". Let's look at an example. +If there is only one version of the constructor, that version will be mapped to a single feature named make. However, if there are overloaded versions of the constructor, then these versions given names starting with "make_from_" and then followed with the argument names from the assembly metadata separated with the conjunction "_and_". Let's look at an example. -The .NET type System.Drawing.Size has an overloaded constructor with two versions. In the System.Drawing assembly metadata, these two constructor versions look like this: +The .NET type System.Drawing.Sizehas an overloaded constructor with two versions. In the System.Drawing assembly metadata, these two constructor versions look like this: void .ctor(int32 width, int32 height) void .ctor(System.Drawing.Point pt) -So the argument names for the first version are width and height . For the second version there is only one argument named pt . The constructor versions as presented to Eiffel programmers as creation procedures look like this: +So the argument names for the first version are width and height. For the second version there is only one argument named pt. The constructor versions as presented to Eiffel programmers as creation procedures look like this: make_from_width_and_height (width: INTEGER; height: INTEGER) make_from_pt (pt: DRAWING_POINT) @@ -41,7 +41,7 @@ Presenting the names in this format handles the conflicts of overloading and pro ===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 Create 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. +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 Create 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. diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/eiffel-class-and-feature-names.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/eiffel-class-and-feature-names.wiki index 9f689577..c5926b4a 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/eiffel-class-and-feature-names.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/eiffel-class-and-feature-names.wiki @@ -153,15 +153,15 @@ 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 language does not allow direct assignment to them from outside the control of their class. So any assignment of the form x. f := y is not valid in Eiffel. To allow client to set values of the attribute f, the producer of the class of which x is an instance would have built a command (a " set_" procedure) to do so. Then the code in a client to set f would look like this: x. set_f ( y). +In Eiffel, the same goals are fulfilled, but a little differently. Simple attributes are well-encapsulated, because the Eiffel language does not allow direct assignment to them from outside the control of their class. So any assignment of the form x. f := y is not valid in Eiffel. To allow client to set values of the attribute f, the producer of the class of which x is an instance would have built a command (a "set_" procedure) to do so. Then the code in a client to set f would look like this: x.set_f (y). -Uniform access is achieved in Eiffel through the way in which clientssee features which are queries. The code " print ( x. count)" applies the query count to the object attached to x and prints the result. You cannot tell by looking at this code whether count is an attribute or a function, that is, whether the count 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 countis 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. +Uniform access is achieved in Eiffel through the way in which clientssee features which are queries. The code " print ( x.count)" applies the query count to the object attached to x and prints the result. You cannot tell by looking at this code whether count is an attribute or a function, that is, whether the count 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 countis 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 get 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 x. f := y. 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 set_ followed by the Eiffel name for 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 x.f := y. 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 "set_" 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 System.Windows.Forms.Form which is available in Eiffel for .NET as WINFORMS_FORM is used by an Eiffel client. System.Windows.Forms.Form has a property Text which is of type System.String. Here the Text property is being set using the set_text feature, and then being recalled by using the query text. diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/similar-types-occurring-both-libraries.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/similar-types-occurring-both-libraries.wiki index 7ab7656e..ed26a85a 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/similar-types-occurring-both-libraries.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/similar-types-occurring-both-libraries.wiki @@ -19,17 +19,17 @@ When we build software that has access to both the rich Eiffel libraries and the 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 STRING ; the .NET assembly contains type System.String , which Eiffel for .NETusers see as SYSTEM_STRING . 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 System.String are immutable. So you cannot append to an instance of System.String . If you want to build a string by appending, you should use an instance of System.Text.StringBuilder to do the appending, then extract the instance of System.String from it. With STRING it is permissible to append, so you don't need a helper like the System.Text.StringBuilder type. +The Eiffel Base Library contains class STRING; the .NET assembly contains type System.String, which Eiffel for .NETusers see as SYSTEM_STRING. 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 System.String are immutable. So you cannot append to an instance of System.String. If you want to build a string by appending, you should use an instance of System.Text.StringBuilder to do the appending, then extract the instance of System.String from it. With STRING it is permissible to append, so you don't need a helper like the System.Text.StringBuilder 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 System.String . +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 System.String. -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 STRING (except for those designed for .NET compliance). +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 STRING (except for those designed for .NET compliance). -In C# and VB.NET, if you specify a quoted string like "Hello World!" in your code, that string will conform to type System.String . If you do the same in Eiffel, then "Hello World!" will be an instance of STRING . In Eiffel terminology, "Hello World!" appearing in source code is a manifest string. +In C# and VB.NET, if you specify a quoted string like "Hello World!" in your code, that string will conform to type System.String. If you do the same in Eiffel, then "Hello World!" will be an instance of STRING. In Eiffel terminology, "Hello World!" appearing in source code is a manifest string. -What all this means to you is that you cannot use an instance of System.String when an instance of STRING is called for, and vice versa. Three out of four of the executable lines in the following code sample are invalid: +What all this means to you is that you cannot use an instance of System.String when an instance of STRING is called for, and vice versa. Three out of four of the executable lines in the following code sample are invalid: local my_string: STRING @@ -44,13 +44,13 @@ What all this means to you is that you cannot use an instance of System.S end -To handle this issue, the Eiffel for .NET class STRING has two features which can be used when a string of the other type is needed. +To handle this issue, the Eiffel for .NET class STRING has two features which can be used when a string of the other type is needed. -The first of these features is a query to_cil which returns an object of type System.String which has a sequence of characters equivalent to that of the STRING to which to_cil is applied. The to_cil can be applied to manifest strings by enclosing the manifest string in parentheses. +The first of these features is a query to_cil which returns an object of type System.String which has a sequence of characters equivalent to that of the STRING to which to_cil is applied. The to_cil can be applied to manifest strings by enclosing the manifest string in parentheses. -The other feature is a creation procedure named make_from_cil which takes as an argument an instance of System.String and initializes its target STRING with a sequence of characters equivalent to that of the argument. +The other feature is a creation procedure named make_from_cil which takes as an argument an instance of System.String and initializes its target STRING with a sequence of characters equivalent to that of the argument. -In the following sample, we use these features of STRING to make all the lines from the previous sample valid. +In the following sample, we use these features of STRING to make all the lines from the previous sample valid. local my_string: STRING @@ -65,8 +65,8 @@ In the following sample, we use these features of STRING to make end -{{note|As shown in the above example, it is necessary to apply to_cil to a manifest string if you are assigning it to a System.String or passing it as an argument where a System.String is called for.
-This is expected to change in a future release. It shall become unnecesary to apply to_cil to manifest strings. Instead, whether a STRING or System.String 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. }} +{{note|As shown in the above example, it is necessary to apply to_cil to a manifest string if you are assigning it to a System.String or passing it as an argument where a System.String is called for.
+This is expected to change in a future release. It shall become unnecesary to apply to_cil to manifest strings. Instead, whether a STRING or System.String 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=== diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/type-organization.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/type-organization.wiki index 62b77cbf..8aa4f7f3 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/type-organization.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/conventions/type-organization.wiki @@ -5,16 +5,16 @@ In any comprehensive object-oriented system, the act of programming results in c ==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 list which contains classes which implement different types of lists, for example, ARRAYED_LIST , LINKED_LIST , SORTED_TWO_WAY_LIST , TWO_WAY_CIRCULAR . At an abstract level all these classes are related to the software model of the notion of "list". +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 list which contains classes which implement different types of lists, for example, ARRAYED_LIST, LINKED_LIST, SORTED_TWO_WAY_LIST, TWO_WAY_CIRCULAR. At an abstract level all these classes are related to the software model of the notion of "list". -The cluster list is actually a subcluster of the cluster structures which contains clusters other than list related to data structures other than lists. Eiffel convention dictates that a cluster should either contain classes or subclusters, but not both. +The cluster list is actually a subcluster of the cluster structures which contains clusters other than list 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 LINKED_LIST 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 structures in subcluster list . +So clusters serve both to categorize and locate classes. So, class LINKED_LIST 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 structures in subcluster list. ==.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 System.Windows.Forms.TextBox as the TextBox type in the context of the windows forms namespace ( System.Windows.Forms ). Particularly, this is in contrast to type System.Web.UI.TextBox which is a TextBox 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. +As .NET programmers, we think of types as being categorized by namespace. For example, we view the type System.Windows.Forms.TextBox as the TextBox type in the context of the windows forms namespace ( System.Windows.Forms). Particularly, this is in contrast to type System.Web.UI.TextBox which is a TextBox 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. @@ -25,11 +25,11 @@ The object model for which Eiffel was designed differs in some ways from the .NE 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 STRING , all the classes upon which STRING depends ( STRING 's suppliers and ancestors) must also be included in the assembly. That's because the Eiffel libraries, unlike the Microsoft .NET libraries like mscorlib and System.Data , are not distributed as shared type libraries. +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 STRING, all the classes upon which STRING depends ( STRING's suppliers and ancestors) must also be included in the assembly. That's because the Eiffel libraries, unlike the Microsoft .NET libraries like mscorlib and System.Data, 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 GUARD_DOG , then in the assembly you'd see an interface called GuardDog , a class called Impl.GuardDog , and a class called Create.GuardDog . Again, this is done for reasons that concern the differences in the object models between Eiffel and .NET. +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 GUARD_DOG, then in the assembly you'd see an interface called GuardDog, a class called Impl.GuardDog, and a class called Create.GuardDog. Again, this is done for reasons that concern the differences in the object models between Eiffel and .NET. -The GuardDog 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 Impl.GuardDog . You create an instance of Impl.GuardDog and attach it to an entity of type GuardDog by calling a routine in the factory class Create.GuardDog . The factory routines will almost always have names that begin with the word " Make ", and represent the creation routines of Eiffel the classes. So in the case of using an instance of GuardDog from a C# class, the code would like this: +The GuardDog 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 Impl.GuardDog. You create an instance of Impl.GuardDog and attach it to an entity of type GuardDog by calling a routine in the factory class Create.GuardDog. The factory routines will almost always have names that begin with the word " Make", and represent the creation routines of Eiffel the classes. So in the case of using an instance of GuardDog from a C# class, the code would like this: { GuardDog aGuardDog = Create.GuardDog.Make(); //Create an instance @@ -39,7 +39,7 @@ The GuardDog interface is what you use when you declare an entity 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 GUARD_DOG might use the following code to create and use an instance. +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 GUARD_DOG might use the following code to create and use an instance. local a_guard_dog: GUARD_DOG -- Declare an entity of the type @@ -49,7 +49,7 @@ Another advantage is that it provides a syntax that is similar to that used to c end -You may have noticed in these examples that even though the type GuardDog was compiled from an Eiffel class, when the C# client uses GuardDog , it uses what would be considered the .NET naming convention for the type GuardDog (vs. GUARD_DOG ) and the method name RollOver (vs roll_over ). What happens here is that when assemblies are produced from Eiffel classes, by default .NET naming standards are used in the assembly. +You may have noticed in these examples that even though the type GuardDog was compiled from an Eiffel class, when the C# client uses GuardDog, it uses what would be considered the .NET naming convention for the type GuardDog (vs. GUARD_DOG) and the method name RollOver (vs roll_over). What happens here is that when assemblies are produced from Eiffel classes, by default .NET naming standards are used in the assembly. diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/adding-class-features.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/adding-class-features.wiki index bbf04260..2cfa74b0 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/adding-class-features.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/adding-class-features.wiki @@ -9,65 +9,67 @@ It is when we add features to a class that we can build the executable code that ==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 feature clause gives us a way to group features in the software text. We have ways to group features more generally, too. Here are some. +In Eiffel, we have several ways of thinking abstractly about features and categorizing them. As you saw in [[Eiffel Classes|Eiffel Classes]] the feature 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]] : indexing - description: Objects that model lists - revision: $Revision: 1.5 $ + description: Objects that model lists + revision: $Revision: 1.5 $ class - OLD_FASHIONED_LIST [G] + OLD_FASHIONED_LIST [G] obsolete "This class is obsolete, use LINKED_LIST [G] instead" inherit - DYNAMIC_LIST [G] + DYNAMIC_LIST [G] create - make + make feature -- Initialization - make is + make -- Create an empty list. - do - before := True - ensure is_before: before - end + do + before := True + ensure + is_before: before + end feature -- Access - item: G is - -- Current item - do - Result := active.item - end + item: G + -- Current item + do + Result := active.item + end - first: like item is - -- Item at first position - do - Result := first_element.item - end - (other features omitted) + 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) + before_constraint: before implies (active = first_element) + after_constraint: after implies (active = last_element) end -The example shows three of the features ( make , item , and first ) 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 OLD_FASHIONED_LIST . In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class: +The example shows three of the features ( make, item, and first) 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 OLD_FASHIONED_LIST. In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class: - inherit - DYNAMIC_LIST [G] + inherit + DYNAMIC_LIST [G] -This means that every feature in DYNAMIC_LIST will be a feature of OLD_FASHIONED_LIST . So one way we can think of features is by their source. +This means that every feature in DYNAMIC_LIST will be a feature of OLD_FASHIONED_LIST. 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. @@ -93,7 +95,7 @@ Also, from the producer standpoint we may view features as whether they work fro ** Procedure -This view can be valuable when a producer is considering performance issues. For example, if there is some class function called count , a producer might investigate whether it is better to leave count as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often. +This view can be valuable when a producer is considering performance issues. For example, if there is some class function called count, a producer might investigate whether it is better to leave count as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often. ===Usage=== @@ -115,59 +117,59 @@ Queries are features that, when applied to an instance, provide a value in respo 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. class - SOME_CLASS + SOME_CLASS create - make, - make_with_arguments + make, + make_with_arguments feature -- Initialization - make is - -- Creation procedure - do - an_attribute := 5 - end + make + -- Creation procedure + do + an_attribute := 5 + end - make_with_arguments (hour: INTEGER; minute: INTEGER; second: INTEGER) is - -- Another creation procedure - do - an_attribute := second + (minute * 60) + (hour * 3600) - 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 + an_attribute: INTEGER + -- An attribute of type INTEGER - another_attribute: INTEGER is 46 - -- A constant attribute + another_attribute: INTEGER = 46 + -- A constant attribute - a_function: STRING is - -- A function without arguments - do - Result := an_attribute.out - end + a_function: STRING is + -- A function without arguments + do + Result := an_attribute.out + end - another_function (an_int: INTEGER): INTEGER is - -- A function with arguments - do - Result := an_attribute + an_int - end + another_function (an_int: INTEGER): INTEGER + -- A function with arguments + do + Result := an_attribute + an_int + end feature -- Basic Operations - a_procedure is - -- A procedure with no arguments - do - an_attribute := an_attribute + 5 - end + a_procedure + -- A procedure with no arguments + do + an_attribute := an_attribute + 5 + end - another_procedure (an_int: INTEGER; another_int: INTEGER) is - -- A procedure with arguments - do - an_attribute := an_attribute + an_int + another_int - 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 @@ -185,11 +187,11 @@ When you write a feature in a class, you typically will include some of the foll Let's dissect one feature and identify its parts: - another_function (an_int: INTEGER): INTEGER is - -- A function with arguments - do - Result := an_attribute + an_int - end + another_function (an_int: INTEGER): INTEGER + -- A function with arguments + do + Result := an_attribute + an_int + end In this feature: @@ -201,9 +203,10 @@ In this feature: * "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 "Result" as an entity name for the value to be returned by the function }} -* "end" ends the feature declaration +{{note|This feature is a function. As a consequence the computation uses the keyword "Result' as an entity name for the value to be returned by the function }} + +* "end" ends the feature declaration ==General Structure of Routines== @@ -222,69 +225,69 @@ 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. - insert_text_header_item (a_label: STRING; - a_width, a_format: INTEGER; insert_after_item_no: INTEGER) is - -- 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 + 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 Using this example, let's identify and discuss the different sections. ===Header Comment=== - -- Insert a text item to the header control - -- after the `insert_item_item_no' item. + -- Insert a text item to the header control + -- after the `insert_item_item_no' item. 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 insert_text_header_item is not obsolete ... but if it were it would include an obsolete clause. This works much like the obsolete part for classes that we saw in [[Eiffel Classes|Eiffel Classes]] : the keyword " obsolete " followed by a manifest string which bears a message to potential reuse consumers: +The feature insert_text_header_item is not obsolete ... but if it were it would include an obsolete clause. This works much like the obsolete part for classes that we saw in [[Eiffel Classes|Eiffel Classes]] : the keyword "obsolete" followed by a manifest string which bears a message to potential reuse consumers: ===Precondition=== - require - exists: exists - label_not_void: a_label /= Void - insert_after_item_no_positive: insert_after_item_no >= 0 + require + exists: exists + label_not_void: a_label /= Void + insert_after_item_no_positive: insert_after_item_no >= 0 -The precondition part of a feature is introduced by the keyword " require ". 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]] . +The precondition part of a feature is introduced by the keyword "require". 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=== - local - hd_item: WEL_HD_ITEM + local + hd_item: WEL_HD_ITEM -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, hd_item is available as type WEL_HD_ITEM during the computation of feature insert_text_header_item . +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, hd_item is available as type WEL_HD_ITEM during the computation of feature insert_text_header_item. {{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=== - 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) + 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) 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]] . @@ -293,12 +296,12 @@ There are other forms that a routine body can take. Here are some examples of so ====External Routines==== - cwin_tooltips_class: POINTER is - external - "C [macro ] : EIF_POINTER" - alias - "TOOLTIPS_CLASS" - end + cwin_tooltips_class: POINTER + external + "C [macro ] : EIF_POINTER" + alias + "TOOLTIPS_CLASS" + end 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. @@ -307,70 +310,70 @@ The routine body above is for an "external" routine. External routines are used ====Once Routines==== - once - Result := some_computation + once + Result := some_computation -This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword " once " rather than do . It contains an computational body that executes only the first time it is called. Upon subsequent calls it has no effect. +This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword "once" rather than do. 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 globals. 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. +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==== - deferred + deferred -The body for a deferred routine is simply the keyword " deferred ". Below is the deferred routine body in the context of an entire feature. +The body for a deferred routine is simply the keyword "deferred". Below is the deferred routine body in the context of an entire feature. - set_position (new_position: INTEGER) is - -- 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 + 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 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=== - ensure - item_count_increased: item_count = old item_count + 1 + ensure + item_count_increased: item_count = old item_count + 1 -The postcondition part of a routine is introduced by the keyword " ensure ". 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]] . +The postcondition part of a routine is introduced by the keyword "ensure". 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 " rescue ", 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. +Our example feature does not have an explicitly coded rescue part. The rescue, introduced by the keyword "rescue", 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. - bind is - -- 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 + 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 ==Making Features Available to Clients== @@ -383,66 +386,66 @@ Control of the export of immediate features (those features introduced in the te feature -- Access - an_attribute: INTEGER - -- An attribute of type INTEGER + an_attribute: INTEGER + -- An attribute of type INTEGER - another_attribute: INTEGER is 46 - -- A constant attribute + another_attribute: INTEGER = 46 + -- A constant attribute - a_function: STRING is - -- A function without arguments - do - Result := an_attribute.out - end + a_function: STRING + -- A function without arguments + do + Result := an_attribute.out + end - another_function (an_int: INTEGER): INTEGER is - -- A function with arguments - do - Result := an_attribute + an_int - end + another_function (an_int: INTEGER): INTEGER + -- A function with arguments + do + Result := an_attribute + an_int + end feature -- Basic Operations - a_procedure is - -- A procedure with no arguments - do - an_attribute := an_attribute + 5 - end + a_procedure + -- A procedure with no arguments + do + an_attribute := an_attribute + 5 + end - another_procedure (an_int: INTEGER; another_int: INTEGER) is - -- A procedure with arguments - do - an_attribute := an_attribute + an_int + another_int - end + another_procedure (an_int: INTEGER; another_int: INTEGER) + -- A procedure with arguments + do + an_attribute := an_attribute + an_int + another_int + end -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. Techically, this +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 - feature -- Basic Operations +feature -- Basic Operations is equivalent to - feature {ANY} -- Basic Operations +feature {ANY} -- Basic Operations -which means that the following features are available to clients which conform to class ANY . And in Eiffel, ANY is the class from which all other classes inherit. As a consequence all classes conform to ANY . +which means that the following features are available to clients which conform to class ANY. And in Eiffel, ANY is the class from which all other classes inherit. As a consequence all classes conform to ANY. Inside the braces is a list of classes which are eligible as clients. - feature {STRING_HANDLER} -- Implementation +feature {STRING_HANDLER} -- Implementation -Features following this example from class STRING will be available to client class STRING_HANDLER and all its proper descendants. +Features following this example from class STRING will be available to client class STRING_HANDLER and all its proper descendants. As stated above, you can put a list of class names in the braces: - feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features +feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features -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 NONE , the class from which no other class can inherit: +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 NONE, the class from which no other class can inherit: - feature {NONE} -- Implementation +feature {NONE} -- Implementation ==Eiffel Instructions and Control Structures== @@ -451,81 +454,81 @@ When you begin to write routines in Eiffel, you will need to understand how to w ===Creation=== - create hd_item.make + create hd_item.make -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 " create ". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity. +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 "create". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity. ===Procedure Call=== - hd_item.set_text (a_label) + hd_item.set_text (a_label) 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 is_open_read is an attribute declared as type BOOLEAN : +In Eiffel, assignment syntax is simple. But depending upon the types involved, what actually happens may need some explanation. Assume here that is_open_read is an attribute declared as type BOOLEAN: - is_open_read := False + is_open_read := False -In this instruction, an attribute of type BOOLEAN named is_open_read is being assigned the value of the manifest boolean " False ". The attribute is_open_read is based on an expanded class BOOLEAN , so that means that the value for False is held in the field for is_open_read . This is in contrast to what happens with reference types. +In this instruction, an attribute of type BOOLEAN named is_open_read is being assigned the value of the manifest boolean "False". The attribute is_open_read is based on an expanded class BOOLEAN, so that means that the value for False is held in the field for is_open_read. This is in contrast to what happens with reference types. - my_string := some_other_string + my_string := some_other_string -In this assignment, we will assume that both entities are type STRING . Because STRING is a reference type, the field for my_string will hold either a reference to an instance of STRING , or it will be Void . When the assignment above executes, then whatever was in the field for my_string is replaced with a reference to the same object that some_other_string refers to. +In this assignment, we will assume that both entities are type STRING. Because STRING is a reference type, the field for my_string will hold either a reference to an instance of STRING, or it will be Void. When the assignment above executes, then whatever was in the field for my_string is replaced with a reference to the same object that some_other_string 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 " x receives y " or " x gets y ". +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 "x receives y" or "x gets y". - x := y + x := y -then the rule says that this assignment is valid only if the type of y conforms to the type of x . +then the rule says that this assignment is valid only if the type of y conforms to the type of x. -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. +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 x and y . +Consider the classes in the declarations for x and y. -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 x and y are expanded classes, you'd be correct. They conform only if they are the same class. +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 x and y 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 y conforms to that x 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]] . +But for reference types, that constraint is unnecessarily restrictive. So, for reference types, the class of y conforms to that x 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=== - if l_c.is_digit then - l_state := 2 - elseif l_c = '-' or l_c = '+' then - l_state := 1 - else - l_state := 3 - end + if l_c.is_digit then + l_state := 2 + elseif l_c = '-' or l_c = '+' then + l_state := 1 + else + l_state := 3 + end -Conditionals in Eiffel use at a minimum the keywords " if ", " then ", and end ".Optionally, they may use the keywords " elseif " and " else ". +Conditionals in Eiffel use at a minimum the keywords "if", "then", and "end". Optionally, they may use the keywords "elseif" and "else". ===Loop=== - 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 + 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 -There is only one structure for loops in Eiffel. In its typical form it uses the four keywords " from ", " until ", " loop ", and " end ". The instructions following the from keyword do any initialization necessary for the loop. After until is a boolean expression which is the exit condition. The loop body after loop 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. +There is only one structure for loops in Eiffel. In its typical form it uses the four keywords "from", "until", "loop", and "end". The instructions following the from keyword do any initialization necessary for the loop. After until is a boolean expression which is the exit condition. The loop body after loop 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. diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/eiffel-classes.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/eiffel-classes.wiki index 5603ab86..a19b4b8e 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/eiffel-classes.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/eiffel-classes.wiki @@ -22,94 +22,95 @@ The code that makes up an Eiffel class is divided into the following parts: All of the above, except Class header, are optional. So the simplest Eiffel class you could build would look like this: class - SIMPLE + SIMPLE end Okay, so class SIMPLE is only interesting in its simplicity. Let's look at an example that is more illustrative: indexing - description: Objects that model lists - revision: $Revision: 1.4 $ + description: Objects that model lists + revision: $Revision: 1.4 $ class - OLD_FASHIONED_LIST [G] + OLD_FASHIONED_LIST [G] obsolete "This class is obsolete, use LINKED_LIST [G] instead" inherit - DYNAMIC_LIST [G] + DYNAMIC_LIST [G] create - make + make feature -- Initialization - make is - -- Create an empty list. - do - before := True - ensure - is_before: before - end + make + -- Create an empty list. + do + before := True + ensure + is_before: before + end feature -- Access - item: G is - -- Current item - do - Result := active.item - end + item: G + -- Current item + do + Result := active.item + end - first: like item is - -- Item at first position - do - Result := first_element.item - end - (other features omitted) + 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) + before_constraint: before implies (active = first_element) + after_constraint: after implies (active = last_element) 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. ===Indexing=== indexing - description: Objects that model lists - revision: $Revision: 1.4 $ + description: Objects that model lists + revision: $Revision: 1.4 $ The indexing 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 indexing does not change the semantics of the class. -The indexing in the class above is typical. It is introduced with the language keyword indexing , and contains two index clauses, each of which is comprised of an index and a single index value. You can code index clauses with indexes that you devise yourself, so there is nothing inherently special about " description " and " revision " 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. +The indexing in the class above is typical. It is introduced with the language keyword indexing, and contains two index clauses, each of which is comprised of an index and a single index value. You can code index clauses with indexes that you devise yourself, so there is nothing inherently special about "description" and "revision" 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=== class - OLD_FASHIONED_LIST [G] + OLD_FASHIONED_LIST [G] -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 deferred , expanded , or external . In our example, the class has none of these markings, so it is an effective class whose instances are access by reference. +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 deferred, expanded, or external. 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 " OLD_FASHIONED_LIST ". +The keyword class is followed by the class name, in this case "OLD_FASHIONED_LIST". -Of the three keywords for header marks, the one which you will encounter most often is deferred . 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. +Of the three keywords for header marks, the one which you will encounter most often is deferred. 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=== class - OLD_FASHIONED_LIST [G] + OLD_FASHIONED_LIST [G] -In this example the class name is followed by the specification of one formal generic parameter " G ". 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 OLD_FASHIONED_LIST we would substitute the class name for the type of objects that we would want to build an OLD_FASHIONED_LIST of. We might make this declaration: +In this example the class name is followed by the specification of one formal generic parameter "G". 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 OLD_FASHIONED_LIST we would substitute the class name for the type of objects that we would want to build an OLD_FASHIONED_LIST of. We might make this declaration: - my_list_of_cats: OLD_FASHION_LIST [CAT] + my_list_of_cats: OLD_FASHION_LIST [CAT] -The entity my_list_of_cats could then be attached at runtime to an OLD_FASHIONED_LIST of objects of type CAT . So the class CAT becomes an actual generic parameter and substitutes for G in the declaration. +The entity my_list_of_cats could then be attached at runtime to an OLD_FASHIONED_LIST of objects of type CAT. So the class CAT becomes an actual generic parameter and substitutes for G 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. @@ -120,98 +121,100 @@ You will learn more about generic classes in the section titled [[Genericity|Gen obsolete "This class is obsolete, use LINKED_LIST [G] instead" - OLD_FASHION_LIST 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 indexing, obsolete has no effect on the semantics of the class. +OLD_FASHION_LISTs 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 indexing, obsolete 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=== inherit - DYNAMIC_LIST [G] + DYNAMIC_LIST [G] -One of the two possible relationships between classes, inheritance is also a powerful software reuse mechanism. In this example class OLD_FASHIONED_LIST declares itself to be a proper descendant of class DYNAMIC_LIST . +One of the two possible relationships between classes, inheritance is also a powerful software reuse mechanism. In this example class OLD_FASHIONED_LIST declares itself to be a proper descendant of class DYNAMIC_LIST. There will be more in the section called . For now though, be aware of two important implications of this declaration: -* Every feature of DYNAMIC_LIST is available to OLD_FASHIONED_LIST and potentially available to its clients. -* Whenever an instance of DYNAMIC_LIST is called for, then an instance of OLD_FASHIONED_LIST will suffice. +* Every feature of DYNAMIC_LIST is available to OLD_FASHIONED_LIST and potentially available to its clients. +* Whenever an instance of DYNAMIC_LIST is called for, then an instance of OLD_FASHIONED_LIST will suffice. ===Creators=== create - make + make -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 make . By convention, creation procedure names begin with the word " make ". +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 make. By convention, creation procedure names begin with the word " make". Let's take a quick look at object creation. Consider this declaration: - my_list_of_cats: OLD_FASHION_LIST [CAT] + my_list_of_cats: OLD_FASHION_LIST [CAT] -Here the entity my_list_of_cats can be attached to an object of type OLD_FASHION_LIST [ CAT ] at runtime. The process of converting my_list_of_cats 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: +Here the entity my_list_of_cats can be attached to an object of type OLD_FASHION_LIST [CAT] at runtime. The process of converting my_list_of_cats 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: - create my_list_of_cats.make + create my_list_of_cats.make -The create keyword is used to introduce a creation instruction. This instruction causes the following four things to happen: -* A shell of a new instance of OLD_FASHION_LIST [ CAT ] is created in memory with a memory field for every attribute +The create keyword is used to introduce a creation instruction. This instruction causes the following four things to happen: +* A shell of a new instance of OLD_FASHION_LIST [CAT] is created in memory with a memory field for every attribute * Each field is initialized with standard default values -** False for type BOOLEAN -** Null character for type CHARACTER +** False for type BOOLEAN +** Null character for type CHARACTER ** The appropriate form of zero for number types -** Void for reference types +** Void for reference types -* Attach the new instance to the entity my_list_of_cats -* Apply the creation procedure make +* Attach the new instance to the entity my_list_of_cats +* Apply the creation procedure make -Once these steps complete successfully, my_list_of_cats will be attached to a valid instance (i.e., an instance in which the class invariant is true) of OLD_FASHIONED_LIST [ CAT ]. +Once these steps complete successfully, my_list_of_cats will be attached to a valid instance (i.e., an instance in which the class invariant is true) of OLD_FASHIONED_LIST [CAT]. ===Features=== feature -- Initialization - make is - -- Create an empty list. - do - before := True - ensure is_before: before - end + make + -- Create an empty list. + do + before := True + ensure + is_before: before + end feature -- Access - item: G is - -- Current item - do - Result := active.item - end + item: G + -- Current item + do + Result := active.item + end - first: like item is - -- Item at first position - do - Result := first_element.item - end + first: like item + -- Item at first position + do + Result := first_element.item + end 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 " feature ". In fact there are two occurrences of feature in this example, each followed by a comment. +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 "feature". In fact there are two occurrences of feature in this example, each followed by a comment. -You may declare multiple feature 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 " Access " contains a set of queries available to clients of the class. +You may declare multiple feature 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 "Access" contains a set of queries available to clients of the class. -Although the words " Initialization " and " Access " are actually in comments after the feature 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 feature clauses already in place for predetermined groups. +Although the words "Initialization" and "Access" are actually in comments after the feature 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 feature 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 Eiffel Base Library. This is a good place to look for examples of well constructed classes. }} +{{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=== invariant - before_constraint: before implies (active = first_element) - after_constraint: after implies (active = last_element) + before_constraint: before implies (active = first_element) + after_constraint: after implies (active = last_element) -Here's the last word in a class definition ... both literally and figuratively. The invariant part, introduced not surprisingly by the keyword " invariant ", 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. + +Here's the last word in a class definition ... both literally and figuratively. The invariant part, introduced not surprisingly by the keyword "invariant", 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]] . diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/genericity.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/genericity.wiki index e3c86d74..61a4b5a5 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/genericity.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/genericity.wiki @@ -3,81 +3,76 @@ [[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 LINKABLE , care for only one instance of the type represented by their formal generic parameter. +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 LINKABLE, 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. Some one 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." +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 item which would return the thing from the list to which a cursor currently points. It might have a command put which would enter some new thing into the list. +Using the idea of polymorphic attachment that we learned in [[Inheritance|Inheritance]] , the producer could build such a class. It might have a query item which would return the thing from the list to which a cursor currently points. It might have a command put which would enter some new thing into the list. -What would be the type of item ? And what would be the type of the argument to put ? +What would be the type of item? And what would be the type of the argument to put? -If the producer wants the class to handle all kinds of things, then the answer must be class ANY , the class from which all others inherit. +If the producer wants the class to handle all kinds of things, then the answer must be class ANY, the class from which all others inherit. class - LIST_OF_THINGS - . - . - + LIST_OF_THINGS + ... feature -- Access - item: ANY - -- The thing currently pointed to by cursor + 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 - . - . + put (new_item: ANY) + -- Add `new_item' at the end of the list + ... 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: - my_cats: LIST_OF_THINGS - -- A list of my cats + my_cats: LIST_OF_THINGS + -- A list of my cats Then you could add individual instances to the list: - fluffy, twinkie: CAT - . - . - my_cats.put (fluffy) - my_cats.put (twinkie) + fluffy, twinkie: CAT + ... + my_cats.put (fluffy) + my_cats.put (twinkie) One problem with this type of list is that the type system will not help you keep from doing something pathological like: - fluffy, twinkie: CAT - thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG - . - . - my_cats.put (fluffy) - my_cats.put (twinkie) - my_cats.put (thor) + 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) Another problem is that to do any CAT things with an item in the list, you must reattach it to a CAT entity. The following is invalid. - my_cats.item.purr -- Is invalid + my_cats.item.purr -- Is invalid This is because "item" is type ANY and although it may be currently attached to an instance of CAT, the static typing system cannot guarantee that. So you must use assignment attempt as we saw in the polymorphism example in [[Inheritance|Inheritance]] . - some_cat: CAT - . - . - some_cat ?= my_cats.item - if some_cat /= Void then - some_cat.purr - end + some_cat: CAT + ... + some_cat ?= my_cats.item + if some_cat /= Void then + some_cat.purr + end -You can see that this type of list has its drawbacks. Of course you could build a LIST_OF_CATS class in which item and the argument for put would be of type CAT . This would let you purr 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. +You can see that this type of list has its drawbacks. Of course you could build a LIST_OF_CATS class in which item and the argument for put would be of type CAT. This would let you purr 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 environements without facilities genericity. @@ -85,74 +80,68 @@ What we would like to have is a way to produce the text of the list class once. ==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 LIST_OF_THINGS class might become a class called LIST which is a list of items of type G . In class LIST we would declare item as type G, as well as the argument to put . +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 LIST_OF_THINGS class might become a class called LIST which is a list of items of type G. In class LIST we would declare item as type G, as well as the argument to put. class - LIST [G] - . - . + LIST [G] + ... feature -- Access - item: G - -- The item currently pointed to by cursor - . + 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 - . - . + put (new_item: G) + -- Add `new_item' at the end of the list + ... -We could declare feature my_cats as a LIST of items of type CAT . By doing so we are providing CAT as an "actual generic parameter" in the declaration. Then we are free to treat the features of LIST as if the class name CAT had been substituted for every occurrence of the formal generic parameter G . +We could declare feature my_cats as a LIST of items of type CAT. By doing so we are providing CAT as an "actual generic parameter" in the declaration. Then we are free to treat the features of LIST as if the class name CAT had been substituted for every occurrence of the formal generic parameter G. - 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 + 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 The following would no longer be valid: - my_cats: LIST [CAT] - -- A list of my cats + my_cats: LIST [CAT] + -- A list of my cats - thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG - - . - . - - my_cats.put (thor) -- Is invalid + thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG + ... + my_cats.put (thor) -- Is invalid ==Constrained Genericity== -The generic class LIST illustrated above is perfectly useful for making typed lists of any type of object. The features of the LIST 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. +The generic class LIST illustrated above is perfectly useful for making typed lists of any type of object. The features of the LIST 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 SORTED_LIST . A SORTED_LIST is a list, of course, but it is special in that it acts upon the elements that it holds to keep them in order. +Take for example the case of a class called SORTED_LIST. A SORTED_LIST 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 SORTED_LIST 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. +A SORTED_LIST 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 SORTED_LIST were defined like we did LIST +If SORTED_LIST were defined like we did LIST class - SORTED_LIST [G] + SORTED_LIST [G] -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 SORTED_LIST , we would add to the formal generic part as follows. +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 SORTED_LIST, we would add to the formal generic part as follows. class - SORTED_LIST [G -> COMPARABLE] + SORTED_LIST [G -> COMPARABLE] -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 COMPARABLE and effect the feature "<". +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 COMPARABLE and effect the feature "<". Here, constrained genericity does two things for us. -* First, it states that any candidate for substitution for G must conform to class COMPARABLE . Typically this means it must inherit from COMPARABLE . -* Second, it allows, within the features of SORTED_LIST , the features of COMPARABLE to be applied to any item which has a type of G . +* First, it states that any candidate for substitution for G must conform to class COMPARABLE. Typically this means it must inherit from COMPARABLE. +* Second, it allows, within the features of SORTED_LIST, the features of COMPARABLE to be applied to any item which has a type of G. diff --git a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/inheritance.wiki b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/inheritance.wiki index 3cb2f0b7..4f314bfe 100644 --- a/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/inheritance.wiki +++ b/documentation/current/platform-specifics/microsoft-windows/net/eiffel-net-language/eiffel-net/inheritance.wiki @@ -11,9 +11,9 @@ Inheritance allows us to make extensions and adaptations to existing software, w ==The Eiffel Inheritance Model== -If class B inherits from class A , then: -* Every feature of A is also a feature of B -* In any case in which an instance of A is called for, then an instance of B will suffice. +If class B inherits from class A, then: +* Every feature of A is also a feature of B +* In any case in which an instance of A is called for, then an instance of B 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. @@ -23,51 +23,51 @@ If a class is deferred, then it is not completely implemented. A class is deferr 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 COMPARABLE from the Eiffel Base Library. A portion of COMPARABLE is shown below: +Consider the deferred class COMPARABLE from the Eiffel Base Library. A portion of COMPARABLE is shown below: deferred class - COMPARABLE + COMPARABLE feature -- Comparison - infix "<" (other: like Current): BOOLEAN is - -- Is current object less than `other'? - deferred - end + infix "<" (other: like Current): BOOLEAN + -- Is current object less than `other'? + deferred + end - infix "<=" (other: like Current): BOOLEAN is - -- Is current object less than or equal to `other'? - do - Result := not (other < Current) - end + infix "<=" (other: like Current): BOOLEAN + -- Is current object less than or equal to `other'? + do + Result := not (other < Current) + end - infix ">" (other: like Current): BOOLEAN is - -- Is current object greater than `other'? - do - Result := other < Current - end + infix ">" (other: like Current): BOOLEAN + -- Is current object greater than `other'? + do + Result := other < Current + end - infix ">=" (other: like Current): BOOLEAN is - -- Is current object greater than or equal to `other'? - do - Result := not (Current < other) - end + infix ">=" (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 - -- 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 + 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 -If you are producing a class that you wish to support basic comparison operators, like "<" and ">", you can have that class inherit from COMPARABLE , which has features which correspond to those operators. The text for COMPARABLE contains eight features. Seven of these are effective and one is deferred. +If you are producing a class that you wish to support basic comparison operators, like "<" and ">", you can have that class inherit from COMPARABLE, which has features which correspond to those operators. The text for COMPARABLE contains eight features. Seven of these are effective and one is deferred. -So through inheritance from COMPARABLE , your class, let's call it WHATZIT , would now have these features available. But how would the features of COMPARABLE know what it means to compare WHATZIT s? +So through inheritance from COMPARABLE, your class, let's call it WHATZIT, would now have these features available. But how would the features of COMPARABLE know what it means to compare WHATZITs? -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 WHATZIT inherits from the COMPARABLE class. +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 WHATZIT inherits from the COMPARABLE class. -When you look closely at the effective features of COMPARABLE , 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. +When you look closely at the effective features of COMPARABLE, 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== @@ -77,101 +77,101 @@ Feature adaptation is an enabling capability, but it is also one that takes some 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 LINKED_QUEUE from the Eiffel libraries. LINKED_QUEUE is an effective class which implements the abstract notion of a QUEUE (a deferred class) with an implementation based on the services provided by LINKED_LIST (an effective class). +In [[Eiffel Classes|Eiffel Classes]] you saw where the inheritance part fits into the class structure. Shown below is a portion of class LINKED_QUEUE from the Eiffel libraries. LINKED_QUEUE is an effective class which implements the abstract notion of a QUEUE (a deferred class) with an implementation based on the services provided by LINKED_LIST (an effective class). class - LINKED_QUEUE [G] + 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 + 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 -Okay ... now calm down ... please. This is an example from a very highly-evolved and sophisticated library which is replete with software reuse. LINKED_QUEUE has two parents and uses considerable feature adaptation. In fact, it uses every feature adaptation option available. The benefit is obvious, though. LINKED_QUEUE class has only seven features actually coded. In total there are only 26 lines of instructions! +Okay ... now calm down ... please. This is an example from a very highly-evolved and sophisticated library which is replete with software reuse. LINKED_QUEUE has two parents and uses considerable feature adaptation. In fact, it uses every feature adaptation option available. The benefit is obvious, though. LINKED_QUEUE 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 LINKED_QUEUE above. +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 LINKED_QUEUE above. -Regardless, let's break LINKED_QUEUE 's inheritance part into chunks and take a look at some of them. +Regardless, let's break LINKED_QUEUE's inheritance part into chunks and take a look at some of them. ===Rename=== - rename - item as ll_item, - remove as ll_remove, - make as ll_make, - remove_left as remove, - put as ll_put + rename + item as ll_item, + remove as ll_remove, + make as ll_make, + remove_left as remove, + put as ll_put -As you might have already guessed, the rename part, introduced oddly enough by the keyword " rename ", is used to rename features. +As you might have already guessed, the rename part, introduced oddly enough by the keyword "rename", 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 item in LINKED_LIST is perfectly usable in LINKED_QUEUE , but must be applied as ll_item . +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 item in LINKED_LIST is perfectly usable in LINKED_QUEUE, but must be applied as ll_item. 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=== - export - {NONE} - all - {ANY} - writable, - extendible, - wipe_out, - readable + export + {NONE} + all + {ANY} + writable, + extendible, + wipe_out, + readable -The new exports part is introduced by the keyword " export ". 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. +The new exports part is introduced by the keyword "export". 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 " all " is used first to say that all features inherited form LINKED_LIST are unavailable to any clients (export to class NONE ). This is typical for a class like LINKED_QUEUE in which the features important to the client come from the deferred parent, in this case QUEUE , and the class LINKED_LIST is used only for implementation. But, it seems that also in this case, the producer felt differently about the features writable , extendible , wipe_out , and readable , and decided the allow clients of ANY type to utilize these features inherited from LINKED_LIST . +In this example, the keyword "all" is used first to say that all features inherited form LINKED_LIST are unavailable to any clients (export to class NONE). This is typical for a class like LINKED_QUEUE in which the features important to the client come from the deferred parent, in this case QUEUE, and the class LINKED_LIST is used only for implementation. But, it seems that also in this case, the producer felt differently about the features writable, extendible, wipe_out, and readable, and decided the allow clients of ANY type to utilize these features inherited from LINKED_LIST. ===Undefine=== - undefine - is_empty, - copy, - is_equal + undefine + is_empty, + copy, + is_equal 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. @@ -182,25 +182,25 @@ Another way you might use undefine is in the case in which you actually want a f ===Redefine=== - redefine - linear_representation, - prune_all, - extend + redefine + linear_representation, + prune_all, + extend 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 linear_representation , for example, that LINKED_QUEUE would have inherited from QUEUE will not be used. Instead LINKED_QUEUE implements its own version of linear_representation . +So, in this example the implementation for linear_representation, for example, that LINKED_QUEUE would have inherited from QUEUE will not be used. Instead LINKED_QUEUE implements its own version of linear_representation. {{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=== - select - remove + select + remove -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 language reference and will not be reproduced here. Justunderstand at this point that it is sometimes necessary to use select to provide the dynamic binding system with an unambiguous choice of features in the presence of polymorphic attachment. +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 language reference and will not be reproduced here. Justunderstand at this point that it is sometimes necessary to use select 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. @@ -208,144 +208,145 @@ You should note also that repeated inheritance can and does occur often without 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 POLYGON and its proper descendants TRIANGLE and RECTANGLE . +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 POLYGON and its proper descendants TRIANGLE and RECTANGLE. So we can make declarations like: - my_polygon: POLYGON - your_polygon: POLYGON - my_triangle: TRIANGLE - my_rectangle: RECTANGLE - another_rectangle: RECTANGLE + my_polygon: POLYGON + your_polygon: POLYGON + my_triangle: TRIANGLE + my_rectangle: RECTANGLE + another_rectangle: RECTANGLE 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 POLYGON . Also, TRIANGLE and RECTANGLE conform to POLYGON . But, importantly, POLYGON does not conform to TRIANGLE or RECTANGLE . This makes sense intuitively, because we know all rectangles and triangles are polygons ... and we also know that not all polygons are rectangles. +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 POLYGON. Also, TRIANGLE and RECTANGLE conform to POLYGON. But, importantly, POLYGON does not conform to TRIANGLE or RECTANGLE. 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: - my_polygon := your_polygon -- Is valid - your_polygon :=my_polygon -- Is valid - my_polygon :=my_rectangle -- Is valid - my_polygon := my_triangle -- Is valid + my_polygon := your_polygon -- Is valid + your_polygon :=my_polygon -- Is valid + my_polygon :=my_rectangle -- Is valid + my_polygon := my_triangle -- Is valid but - my_rectangle := my_polygon -- Is not valid - my_triangle := my_polygon -- Is not valid + my_rectangle := my_polygon -- Is not valid + my_triangle := my_polygon -- Is not valid and of course - my_rectangle := my_triangle -- Is not valid + my_rectangle := my_triangle -- Is not valid Consider now the assignment below which is valid. - my_polygon := my_rectangle + my_polygon := my_rectangle -After an assignment like this executes the entity my_polygon will be holding at runtime a reference to an instance of a type which is not a direct instance of its declared type POLYGON . But conformance ensures us that, although it may not be a direct instance, it will indeed by an instance. (all rectangles are polygons). +After an assignment like this executes the entity my_polygon will be holding at runtime a reference to an instance of a type which is not a direct instance of its declared type POLYGON. 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 " my_polygon " 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. +Depending upon how many different types of polygons get modeled in classes, the entity "my_polygon" 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 POLYGON is a query perimeter which returns an instance's perimeter. The producer of POLYGON may have implemented perimeter 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 perimeter feature to any polygon. Let's print some perimeters: +Suppose that one of the features of POLYGON is a query perimeter which returns an instance's perimeter. The producer of POLYGON may have implemented perimeter 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 perimeter feature to any polygon. Let's print some perimeters: - print (my_polygon.perimeter) - print (my_triangle.perimeter) - print (my_rectangle.perimeter) + print (my_polygon.perimeter) + print (my_triangle.perimeter) + print (my_rectangle.perimeter) - TRIANGLE and RECTANGLE 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 width and height the lengths of the sides. +TRIANGLE and RECTANGLE 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 width and height the lengths of the sides. -Armed with these RECTANGLE -specific features, the producer of RECTANGLE may say,"Now I no longer have to depend upon that crude implementation of perimeter that is inherited from POLYGON . I can build an efficient RECTANGLE -specific implementation of perimeter , based on the knowledge that for all RECTANGLE s perimeter = 2*(width+height)" +Armed with these RECTANGLE-specific features, the producer of RECTANGLE may say, "Now I no longer have to depend upon that crude implementation of perimeter that is inherited from POLYGON. I can build an efficient RECTANGLE-specific implementation of perimeter, based on the knowledge that for all RECTANGLEs perimeter = 2*(width+height)" -To implement this specialized version of perimeter , the producer of RECTANGLE must add the feature to the class, but also must list its name in the " redefine " part of the RECTANGLE 's inheritance clause. +To implement this specialized version of perimeter, the producer of RECTANGLE must add the feature to the class, but also must list its name in the "redefine" part of the RECTANGLE's inheritance clause. -class RECTANGLE +class + RECTANGLE inherit - POLYGON - redefine - perimeter - end + POLYGON + redefine + perimeter + end . . feature - perimeter: REAL is - -- Sum of lengths of all sides - do - Result := 2 * (width + height) - end + perimeter: REAL + -- Sum of lengths of all sides + do + Result := 2 * (width + height) + end You would expect then, that this version of perimeter would be executed in the following context: - print (my_rectangle.perimeter) + print (my_rectangle.perimeter) But what makes this interesting is that even in the context below - my_polygon := my_rectangle - print (my_polygon.perimeter) + my_polygon := my_rectangle + print (my_polygon.perimeter) -in which perimeter is being applied to a entity declared as POLYGON , the specialized version of perimeter from RECTANGLE is being used. It would be impossible to ensure at compile time which version of perimeter 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". +in which perimeter is being applied to a entity declared as POLYGON, the specialized version of perimeter from RECTANGLE is being used. It would be impossible to ensure at compile time which version of perimeter 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 perimeter to my_polygon No matter which of the types of polygons is attached to my_polygon , there will be a perimeter feature that will work. +Static typing tells us at compile time that it is safe to apply perimeter to my_polygon No matter which of the types of polygons is attached to my_polygon, there will be a perimeter feature that will work. -Dynamic binding tells us that when we apply perimeter , we know that the most appropriate version of the feature will get applied at runtime. +Dynamic binding tells us that when we apply perimeter, we know that the most appropriate version of the feature will get applied at runtime. ===Assignment Attempt=== Now let's add another situation. Consider the code below: - my_polygon := my_rectangle - print (my_polygon.perimeter) - print (my_polygon.width) -- Is invalid + my_polygon := my_rectangle + print (my_polygon.perimeter) + print (my_polygon.width) -- Is invalid -We could apply perimeter to my_polygon and everything is fine ... we even get RECTANGLE 's specialized version of the feature. But it is invalid for us to try to apply width to my_polygon even though we feel (with rather strong conviction) that at this point in execution, my_polygon will be attached to an object of type RECTANGLE , and we know that width is a valid query on RECTANGLE s. +We could apply perimeter to my_polygon and everything is fine ... we even get RECTANGLE's specialized version of the feature. But it is invalid for us to try to apply width to my_polygon even though we feel (with rather strong conviction) that at this point in execution, my_polygon will be attached to an object of type RECTANGLE, and we know that width is a valid query on RECTANGLEs. -The reason follows. When we declared my_polygon as type POLYGON , we made a deal that says that the only features that can be applied to my_polygon are the features of POLYGON . 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. +The reason follows. When we declared my_polygon as type POLYGON, we made a deal that says that the only features that can be applied to my_polygon are the features of POLYGON. 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. - print (my_polygon.width) -- Is invalid + print (my_polygon.width) -- Is invalid -But in the case above, the guarantee cannot be made. my_polygon is declared with class POLYGON which has no width feature, despite the fact that some of its proper descendants might. +But in the case above, the guarantee cannot be made. my_polygon is declared with class POLYGON which has no width feature, despite the fact that some of its proper descendants might. -Does this mean that we can never do RECTANGLE things with this instance again, once we have attached it to my_polygon ? +Does this mean that we can never do RECTANGLE things with this instance again, once we have attached it to my_polygon? -No. There is a language facility called the "assignment attempt" which will come to our rescue. The assignment attempt will allow us safely to attach our instance back to an entity typed as RECTANGLE . After doing so, we are free use RECTANGLE features. +No. There is a language facility called the "assignment attempt" which will come to our rescue. The assignment attempt will allow us safely to attach our instance back to an entity typed as RECTANGLE. After doing so, we are free use RECTANGLE features. - my_polygon := my_rectangle - print (my_polygon.perimeter) - another_rectangle ?= my_polygon - print (another_rectangle.width) + my_polygon := my_rectangle + print (my_polygon.perimeter) + another_rectangle ?= my_polygon + print (another_rectangle.width) The assignment attempt uses the syntax "?=", versus the ":=" of assignment. -This is significant ... as is the name assignment attempt. The reason is that it is possible in some context that my_polygon will be attached to something other than a rectangle when we do this: +This is significant ... as is the name assignment attempt. The reason is that it is possible in some context that my_polygon will be attached to something other than a rectangle when we do this: - another_rectangle ?= my_polygon + another_rectangle ?= my_polygon -If it were true that my_polygon were attached to an instance of say TRIANGLE when the assignment attempt above was executed, then another_rectangle would be left as a Void reference. As a consequence, it is prudent to check to see if the attachment has actually been made before trying to apply features. Applying a feature to a Void reference will cause an exception. Almost always, assignment attempt will be used in the following manner: +If it were true that my_polygon were attached to an instance of say TRIANGLE when the assignment attempt above was executed, then another_rectangle would be left as a Void reference. As a consequence, it is prudent to check to see if the attachment has actually been made before trying to apply features. Applying a feature to a Void reference will cause an exception. Almost always, assignment attempt will be used in the following manner: - my_polygon := my_rectangle - print (my_polygon.perimeter) - another_rectangle ?= my_polygon - if another_rectangle /= Void then -- Make sure assignment happened - print (another_rectangle.width) - end + my_polygon := my_rectangle + print (my_polygon.perimeter) + another_rectangle ?= my_polygon + if another_rectangle /= Void then -- Make sure assignment happened + print (another_rectangle.width) + end