IBM® Rational® Modeling Extension for Microsoft® .NET is an extension into Rational Modeling tools to aid .NET application developers design their applications. At the time of writing this article, Rational Modeling Extension for Microsoft .NET supports modeling of C# applications. The previous two parts of this article series explained basic modeling concepts for C# application. This article, the third in the series, will describe the steps to model the following C# concepts using Rational Modeling Extension for Microsoft .NET
- Arrays
- Pointers
- Partial types
- Generics
This article will discuss the usage of a mapping model to direct code generation into files other than default ones.
This article covers advanced modeling concepts for C# application developers. The article presumes that you are already familiar with basic C# modeling concepts covered in the other articles in this series -- Modeling C# applications using Rational Modeling Extension for Microsoft .NET: Part 1 and Modeling C# applications using Rational Modeling Extension for Microsoft .NET: Part 2-- see Resources. All of the following sections will require you to have created a model and imported a C# profile, as explained in Part 1.
C# arrays can be single dimensional, multi-dimensional, or jagged arrays. When you declare an array type, the size of the array is not specified. To model an array, use the <<CSharp Array>> stereotype on the UML property (attributes) or UML method parameters. Once the stereotype is applied, you can specify the value for the rank_specification stereotype property, which denotes whether an array is 1-dimensional, multi-dimensional or jagged array.
To better understand, model a UML class named ArraysExample having three attributes that represent single-dimensional, multi-dimensional, and jagged (array of arrays) arrays. To do this, follow these steps.
- Create a UML class and name it ArrayExample. Make sure that the C# profile is applied on the UML model.
-
Create a UML attribute in the class and name it singleDimArray. Set its Type to C#
<Primitive Type>intand its Default Value tonew int[5]. -
Apply the <<CSharp Array>> stereotype and set the stereotype property rank_specification to
[ ]. -
Similarly, create a UML attribute named multiDimArray with Default Value as
new int [ 6, 4]and set the rank_specifications stereotype property to[ , ]. -
In the same manner create a UML attribute named jaggedArray with Default Value as
new int[3][ ]and set the rank_specifications stereotype property to[ ] [ ], as shown in Figure 1.
Figure 1. Modeling C# arrays using the <<CSharp Array>> stereotype
In C#, pointers can be used in an unsafe context. An unsafe context is enabled by using the unsafe modifier in the declaration of a type or member. In order to model an unsafe context in UML, set the unsafe stereotype property to true. The unsafe stereotype property is available in stereotypes that can be applied on UML Class, Interface, attribute, or operation. For example, in order to use pointers in an unsafe context, you can set the unsafe modifier in any of <<CSharp Class>>, <<CSharp Interface>>, <<CSharp Method>>, <<CSharp Field>>, <<CSharp Indexer>>, and so on to true.
As an example, you will model a class Node for a binary tree, with 2 pointers to the left and right child nodes. To do this, follow these steps.
-
Create a UML class and name it Node. Since you need to use the
unsafemodifier, apply the <<CSharp Class>> stereotype and set theunsafestereotype property totrue. -
Now add an attribute to the class and name it left. Set its Type to Node. Apply the stereotype <<CSharp Pointer>> and set its indirection_specification property to
*. -
In a similar manner, add another attribute (pointer) with the name right. You have created two pointers of type
Node *in the Node class, as shown in Figure 2.
Figure 2. Modeling C# Pointers using <<CSharp Pointer>> stereotype
In C#, it is possible to give partial definitions of classes, interfaces, and structures spread across multiple files. Basically the concept of partial types allows you (as the application developer) to define parts of a type across multiple files by using the keyword partial with each definition. By using the concept of partial types, the developer can encapsulate different perspectives (data or behavior) of an object in different files. Finally, the actual type is the union of all of the different definitions. For more information on partial types, please refer to the C# language specification.
This article will now show you how to define partial types in Rational Modeling Extension for Microsoft .NET. To start defining a partial type, create the corresponding UML type (class or interface) in the required package. You may use the <<CSharp Struct>> stereotype on the UML class if you are defining a partial struct. This UML type will have the same name as its equivalent C# construct, but will not have any attributes or operations.
Basically, this type will have empty definition. In order to define individual definitions of this partial type, you create as many UML types as the number of definitions, and give some arbitrary but meaningful names to them. The names do not matter for these individual UML types, which represent partial definitions. Each of these types will contain the attributes, operations, and so on corresponding to the partial definition being represented by them. At the end, you need to create a UML dependency relationship stereotyped as <<CSharp Partial>>, starting from each individual definition to the UML type with an empty definition (the type whose name is the same as the C# partial type, but with an empty definition).
To better understand the concept, model an ATM Machine with two perspectives -- one from the user perspective and the other from the bank perspective -- and model these perspectives as two partial definitions.
If you were to define the ATM machine from the user perspective, you would think of operations such as the following:
- Insert ATM card
- Receive PIN
- Disburse money
When you look at ATM machine from bank perspective, on the other hand, you would think of operations such as:
- Validate the card
- Validate the PIN
- Reduce the balance in the user account
Both of these perspectives are defining the ATM machine, but from different angles. Therefore, the ATM machine on the whole has all of the operations from both perspectives. You will now take a look at the steps required to model the partial definitions of the ATM machine.
- Create a UML class and name it ATM_Machine.
- Create another UML class and name it ATM_UserPerspective. Next, create a UML dependency relationship from this class to ATM_Machine. Also, apply the stereotype <<CSharp Partial>> on the dependency relationship. This indicates that ATM_UserPerspective is one of the partial definitions of the class ATM_Machine.
- Add UML operations such as insertCard(String cardNumber), enterPIN(int PIN), enterAmount(long amount), and disburseCash( ), and so on to ATM_UserPerspective.
- Now create another UML class named ATM_BankPerspective, and create a UML dependency relationship from this class again to ATM_Machine. Apply the stereotype <<CSharp Partial>> on the dependency relationship to indicate that ATM_BankPerspective is another partial definition of the class ATM_Machine, as shown in Figure 3.
-
Now add UML operations such as validateCard( ), checkAccountBalance( ), reduceBalance(String accountNumber, long amount). and so on to ATM_BankPerspective.
Note: When you run a UML-to-C# transform on a model containing partial types, each partial definition is generated in a file whose name is the same as the definition name, but the actual type in that file will have the name of the corresponding partial type (the name of the empty UML type where the UML dependency relationship ends) with the partial keyword. To re-direct the code generation of the individual definitions into a different file, use a mapping model (as described in a later section).
Figure 3. Modeling C# partial types using the <<CSharp Partial>> stereotype
In C#, you (as a developer) can define generics for classes, interfaces, structs, delegates, and methods. This article will discuss how to model each of these with examples. Before you proceed, though, you need to understand two aspects of C#: a generic declaration and a generic instantiation.
A generic declaration only defines the template of a type, delegate, or a method. The usage of the generic requires instantiation of generic by providing values to the generic (or template) parameters.
To understand the concept, consider an example. Try to model a simple Library system that has a number of shelves, and each shelf has a number of books that are arranged in a sorted order based on their title. Since list and sorted lists can be used to store any object, you will model them as generics and instantiate them to store specific objects (books). To model this, you will define a generic list and use it to define a sorted list. The sorted list will be instantiated to store information on books.
The code being represented in the above example is shown in Listing 1.
Listing 1. Sample code for the library system modeled using generics
using System;
public class List<T>
{
public bool isEmpty()
{
return true;
}
public int getSize()
{
return 0;
}
public T get(int index)
{
return null;
}
}
public class SortedList<X> : List<X>
{
public void sort(bool ascendingOrder)
{
}
}
public class Book
{
}
public class Shelf
{
SortedList<Book> sortedBooks;
}
public class Library
{
List<Shelf> shelves;
}
|
The steps that you need to perform to create the model (Listing 1) of a library system are as follows.
-
Create a class named List. Right-click the class and add a template parameter with name T. Add operations such as
isEmpty():bool ,getSize():int. -
You will also need to create a method that uses the template parameter. Add a method called
get()with return type as the template parameterTfrom the method (as shown in Figure 4), and an input parameter named index whose type isint.
You can also use the template parameter T for an attribute’s type, in the same way as it was used for the method’s return type.
Figure 4. Selecting the template parameter as the return type of the method
- Similarly, create another class named SortedList and add a template parameter X. Because you want SortedList to extend List, and to use List you need to create an instantiation of the List class, create a placeholder class.
This placeholder class should represent the instantiation of List by binding the template parameter T to the template parameter of X. To accomplish this, create a class named ListBase and add a binding relationship from ListBase to List, and then bind the parameter T to X, as shown in Figure 5.
Figure 5. Binding template parameter
-
Add a method like
sort(ascendingOrder:bool)to SortedList and create a generalization from SortedList to ListBase, as shown in Figure 6.
Figure 6. Generic Class instantiation by using bind relationship
- Create UML classes named Shelf and Book.
-
Now, model a collection of sorted books in a shelf. For this, create a binding class SortedBooksList with a binding to SortedList by setting X to Book. Create a one-directional association from Shelf to SortedBooksList and name the association end sortedBooks. This represents the attribute declaration
SortedList<Book> sortedBooks;shown in Listing 1. -
Create a template binding class named ShelvesList with a binding to the List class by setting the parameter T to Shelf. This represents
List<Shelf>. -
Finally, create a Library class with an association to ShelvesList and name the association end as shelves. This represents the attribute declaration
List<Shelf> shelves;in the Library class of Listing 1.
The previous example covered the concepts of generic declaration and instantiation. Generic methods are similarly modeled by adding template parameters on the UML operation.
A mapping model is used while configuring C# transformations. It is useful when you want to control the physical organization of your source code, which may be different from the default. For example, by default the UML-to-C# transform generates a class in a file with the same name as the class. But if you want two or more classes to exist in the same file, then you could use a mapping model to specify the hierarchy and contents of the source files.
In addition, when you use partial classes, a mapping model is used to direct each definition of the partial class into a file based on your requirements. Basically, a mapping model is nothing but another UML model that captures information on the physical organization of the source code. It is a model usually consisting of packages and artifacts, with manifestations to UML types from the logical design model of the application.
The UML packages in a mapping model represent folders on a file system, and the UML artifacts within the UML packages represent source files in those folders. The artifact should be given the source file name ending with an extension of .cs Each artifact has one or more manifestations, depending on the number of the classes or types to be contained in that file.
For example, in the library system discussed in the previous section, suppose you want this configuration:
- The classes Library, Shelf, and Book in a single file named LibrarySystem.cs
- The LibrarySystem.cs file in a folder named library
- All the generics (List, SortedList) to be generated in a file named LibraryGenerics.cs
- This file under a folder hierarchy library/generics/collections
To do this, create a mapping model as follows:
- Create a UML package named library in the model. This represents the folder library under the C# project root.
- Create an artifact named LibrarySystem.cs under the package library. This represents the file LibrarySystem.cs under the folder library.
- Create three manifestation relationships from the artifact created above to the UML classes Library, Shelf ,and Book from the logical design model of the application.
-
Create a UML package named generics under the
librarypackage. This represents the folder library/generics under the C# project. -
Create a UML package named collections under the
genericspackage. This represents the folder library/generics/collections under the C# project. -
Create an artifact under the
collectionspackage and name it LibraryGenerics.cs - Create two manifestation relationships from the above artifact to the List and SortedList classes from the logical design model.
You can specify this model as the mapping model in the transformation configuration editor. It will be used by the UML-to-C# transform to determine the directory structure of source code during code generation, and the model will also be updated when the C#-to-UML transform is run.
This article has discussed advanced C# modeling topics. It has outlined the concepts of modeling C# pointer and array types. Also, after reading this article, you should be able to model partial and generic types, and use a mapping model to design the file system view of the source code.
Learn
- Read
Modeling C# applications using Rational Modeling Extension for .NET,
Part 1.
- Read
Modeling C# applications using Rational Modeling Extension for .NET,
Part 2.
- Find out more about
IBM Rational Modeling Extension for Microsoft .NET
on developerWorks..
- To learn how to import a C# project, read
IBM Rational Modeling
Extension for Microsoft .NET: Visualizing .NET applications, an IBM®
developerWorks® article by Lalitha Kishore and Darpan Saini (March
2007).
- Visit the
IBM Rational Modeling Extension for Microsoft .NET
area on developerWorks for technical documentation, how-to articles, education,
downloads, and product information.
- Visit the
Rational Software Architect
area on developerWorks for technical documentation, how-to articles, education,
downloads, and product information.
- Subscribe to the
developerWorks Rational zone newsletter.
Keep up with developerWorks Rational content. Every other week, you'll
receive updates on the latest technical resources and best practices for the
Rational Software Delivery Platform.
- Browse the
technology bookstore
for books on these and other technical topics.
Get products and technologies
- Download
trial versions of IBM Rational software.
- Download the trial
version of
Rational Modeling Extension for MS .NET.
- Download
IBM product evaluation versions
and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- Check out
developerWorks blogs
and
get involved in the
developerWorks community.
-
Rational Software Architect, Data Architect, Software Modeler, Application Developer and Web Developer forum: Ask questions about Rational Software Architect.

Rajeshwari Rajendra is a software engineer in the IBM India Software Lab, working on the Rational Systems Developer team. Her expertise includes XML query technologies, such as XPath and XQuery, and UML modeling and transformations (specifically, C# and C++). She has a Master's degree (MCA) from Bangalore University, India.




