The evolution in versions 2.0 and 3.0

As we see, even from the very beginning, the Hejlsberg's team started with a complete, flexible, and modern platform, capable of being extended in many ways as technology evolves. This intention became clear since version 2.0.

The first actual fundamental change that took place in the language was the incorporation of Generics. Don Syme, who would later on lead the team that created the F# language, was very active and led this team as well, so it was ready for version 2.0 of the .NET Framework (not just in C# but in C++ and VB.NET as well).

Generics

The purpose of generics was mainly to facilitate the creation of more reusable code (one of the principles of OOP, by the way). The name refers to a set of language features that allow classes, structures, interfaces, methods, and delegates to be declared and defined with unspecified or generic type parameters instead of specific types (see https://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx, for more details).

So, you can define members in a sort of abstract definition, and later on, at the time of using it, a real, concrete type will be applied.

The basic .NET classes (BCL) were enhanced in the System namespace and a new System.Collections.Generic namespace was created to support this new feature in depth. In addition, new support methods were added to ease the use of this new type, such as Type.IsGenericType (obviously, to check types), Type.GetGenericArguments (self-descriptive), and the very useful Type.MakeGenericType, which can create a generic type of any kind from a previous nonspecified declaration.

The following code uses the generic type definition for a Dictionary (Dictionary<,>) and creates an actual (build) type using this technique. The relevant code is the following (the rest, including the output to the console is included in Demo_02_03):

// Define a generic Dictionary (the
// comma is enough for the compiler to infer number of
// parameters, but we didn't decide the types yet.
Type generic = typeof(Dictionary<,>);
ShowTypeData(generic);

// We define an array of types for the Dictionary (Key, Value)
// Key is of type string, and Value is of -this- type (Program)
// Notice that types could be -in this case- of any kind
Type[] typeArgs = { typeof(string), typeof(Program) };

// Now we use MakeGenericType to create a Type representing
// the actualType generic type.
Type actualType = generic.MakeGenericType(typeArgs);
ShowTypeData(actualType);

As you see, MakeGenericType expects an array of (concrete) types. Later on (not in the preceding code), we use GetGenericTypeDefinition, IsGenericType, and GetGenericArguments in order to introspect the resulting types and present the following output in the console:

Generics

So, we have different ways to declare generics with identical results as far as the operations in the code are concerned.

Obviously, manipulating already constructed generic types is not the only possibility, since one of the main goals of generics is to avoid casting operations by simplifying the work with collections. Up until version 2.0, collections could only hold basic types: integers, longs, strings, and so on, along with emulating different types of data structures, such as stacks, queues, linked lists, and so on.

Besides this, Generics have another big advantage: you can write methods that support working with different types of arguments (and return values) as long as you provide a correct way to handle all possible cases.

Once again, the notion of contract will be crucial here.

Creating custom generic types and methods

Other useful feature is the possibility to use custom generic types. Generic types and the support for optional values through the System.Nullable<T> type were, for many developers, two of the most important features included in version 2.0 of the language.

Imagine you have a Customer class, which your application manages. So, in different use cases, you will read collections of customers and perform operations with them. Now, what if you need an operation such as Compare_Customers? What would be the criteria to use in this case? Even worse, what if we would like to use the same criteria with different types of entities, such as Customer and Provider?

In these cases, some characteristics of generics come in handy. To start with, we can build a class that has an implementation of the IComparer interface, so we establish beyond any uncertainty what the criteria to be used is in order to consider customer C1 bigger or smaller than customer C2.

For instance, if the criteria is only Balance, we can start with a basic Customer class, to which we add a static method in order to generate a list of random customers:

public class Customer
{
  public string Name { get; set; }
  public string Country { get; set; }
  public int Balance { get; set; }
  public static string[] Countries = { "US", "UK", "India", "Canada", 	    "China" };
  public static List<Customer> customersList(int number)
  {
    List<Customer> list = new List<Customer>();
    Random rnd = new Random(System.DateTime.Now.Millisecond);
    for (int i = 1; i <= number; i++)
    {
      Customer c = new Customer();
      c.Name = Path.GetRandomFileName().Replace(".", "");
      c.Country = Countries[rnd.Next(0, 4)];
      c.Balance = rnd.Next(0, 100000);
      list.Add(c);
    }
    return list;
  }
}

Then, we build another CustomerComparer class, which implements the IComparer interface. The difference is that this comparison method is a generic instantiation customized for the Customer objects, so we have the freedom of implementing this scenario just in the way that seems convenient for our logic.

In this case, we're using Balance as an ordering criteria, so that we would have the following:

public class CustomerComparer : IComparer<Customer>
{
  public int Compare(Customer x, Customer y)
  {
    // Implementation of IComparer returns an int
    // indicating if object x is less than, equal to or
    // greater than y.
    if (x.Balance < y.Balance) { return -1; }
    else if (x.Balance > y.Balance) return 1;
    else { return 0; } // they're equal
  }
}

We can see that the criteria used to compare is just the one we decided for our business logic. Finally, another class, GenericCustomer, which implements an entry point of the application, uses both classes in this manner:

public class GenericCustomers
{
  public static void Main()
  {
    List<Customer> theList = Customer.customersList(25);
    CustomerComparer cc = new CustomerComparer();
    // Sort now uses our own definition of comparison
    theList.Sort(cc);
    Console.WriteLine(" List of customers ordered by Balance");
    Console.WriteLine(" " + string.Concat(Enumerable.Repeat("-", 36)));
    foreach (var item in theList)
    {
      Console.WriteLine(" Name: {0},  Country: {1}, 	 Balance: {2}",
      item.Name, item.Country, item.Balance);
    }
    Console.ReadKey();
  }
}

This produces an output of random customers order by their balance:

Creating custom generic types and methods

This is even better: we can change the method so that it supports both customers and providers indistinctly. To do this, we need to abstract a common property of both entities that we can use for comparison.

If our implementation of Provider has different or similar fields (but they're not the same), it doesn't matter as long as we have the common factor: a Balance field.

So we begin with a simple definition of this common factor, an interface called IPersonBalance:

public interface IPersonBalance
{
  int Balance { get; set; }
}

As long as our Provider class implements this interface, we can later create a common method that's able to compare both objects, so, let's assume our Provider class looks like this:

public class Provider : IPersonBalance
{
  public string ProviderName { get; set; }
  public string ShipCountry { get; set; }
  public int Balance { get; set; }

  public static string[] Countries = { "US", "Spain", "India", "France", "Italy" };
  public static List<Provider> providersList(int number)
  {
    List<Provider> list = new List<Provider>();
    Random rnd = new Random(System.DateTime.Now.Millisecond);
    for (int i = 1; i <= number; i++)
    {
      Provider p = new Provider();
      p.ProviderName = Path.GetRandomFileName().Replace(".", "");
      p.ShipCountry = Countries[rnd.Next(0, 4)];
      p.Balance = rnd.Next(0, 100000);
      list.Add(p);
    }
    return list;
  }
}

Now, we rewrite the Comparer method to be a GenericComparer class, capable of dealing with both types of entities:

public class GenericComparer : IComparer<IPersonBalance>
{
  public int Compare(IPersonBalance x, IPersonBalance y)
  {
    if (x.Balance < y.Balance) { return -1; }
    else if (x.Balance > y.Balance) return 1;
    else { return 0; }
  }
}

Note that in this implementation, IComparer depends on an interface, not on an actual class, and that this interface simply defines the common factor of these entities.

Now, our new entry point will put everything together in order to obtain an ordered list of random Provider classes that uses the common comparison method just created:

public static void Main()
{
  List<Provider> providerList = Provider.providersList(25);
  GenericComparer gc = new GenericComparer();
  // Sort now uses our own definition of comparison
  providerList.Sort(gc);
  Console.WriteLine(" List of providers ordered by Balance");
  Console.WriteLine(" " + ("").PadRight(36, '-'));
  foreach (var item in providerList)
  {
    Console.WriteLine(" ProviderName: {0}, S.Country: {1}, 	 Balance: {2}",
    item.ProviderName, item.ShipCountry, item.Balance);
  }
  Console.ReadKey();
}

In this way, we obtain an output like what is shown in the following figure (note that we didn't take much care of formatting in order to focus on the process):

Creating custom generic types and methods

The example shows how generics (and interfaces: also generic) come to our rescue in these types of situations, and—as we'll have the opportunity to prove when talking about implementations of design patterns—this is key to facilitating good practice.

So far, some of the most critical concepts behind generics have been discussed. In up coming chapters, we'll see how other aspects related to generics show up. However, the real power comes from joining these capabilities with two new features of the language: lambda expressions and the LINQ syntax.

Lambda expressions and anonymous types

For a bit, let's review what happens when we create a new, anonymous type by invoking the new operator, followed by a description of the object:

// Anonymous object
var obj = new { Name = "John", Age = 35 };

The compiler correctly infers the non-declared type to be anonymous. Actually, if we use the disassembler tool we saw in the previous chapter, we'll discover how the compiler assigns a default name to this class (f_AnonymousType0`2):

Lambda expressions and anonymous types

Also, we can see that a special constructor has been created along with two private fields and two access methods (get_Age and get_Name).

These kind of objects are especially suitable when we deal with data coming from any source, and we filter the information vertically (that is, we don't require all fields but just a few, or maybe even one).

The resulting objects coming from such a query are not previously defined anywhere in our code, since every different query would required a customized definition.

Lambda expressions

With that in mind, a lambda expression is just an anonymous function, which is expressed in a different syntax that allows you to pass such a function as an argument, much in the style of functional programming languages, such as JavaScript.

In C# 3.0, lambda expressions appeared with a simplified syntax that uses a lambda operator (=>). This operator divides the defined function into two parts: the arguments to the left and the body to the right; for example, take a look at this:

( [list of arguments] ) => { [list of sentences] }

This admits certain variations, such as the omission of parenthesis in the list of arguments and the omission of curly brackets in the body as long as the compiler is able to infer the details, types involved, and so on.

Since the preceding declaration is a delegate's definition, we can assign it to any delegate's variable, and so we can express the condition used when finding the divisible numbers by 3 or 7 in a much neater way:

DivisibleBy3Or7 ed3 = x => ((x % 3 == 0) || (x % 7 == 0));

That is, variable ed3 is assigned a lambda expression that receives an element (an int, in this case) and evaluates the body function, which calculates the same numbers as we did earlier. Note that the body function is not enclosed in curly brackets because the definition is clear enough for the compiler.

So, operating in this manner, there is no need to declare separated methods, and we can even pass one of these expressions as an argument to a method that accepts it like many of the generic collections do.

At this point, we start to see the power of all this when used in conjunction with generic collections. From version 3.0 of .NET framework, generic collections include a bunch of methods that admit lambda expressions as arguments.

It's all about signatures

The .NET framework team, however, went a bit deeper. If you abstract the possible signatures behind any delegate, you can categorize them in three blocks depending on the return value:

  • Delegates with no return value (called actions and defined with the Action keyword)
  • Delegates that return a Boolean (now called predicates, such as in logic, but defined with the Func keyword)
  • The rest of delegates, returning any type (also defined with the Func keyword)

So, these three reserved words became part of the C# syntax, and all the generic methods we find in collections will ask us for one of these three types of delegates. A simple look at one of these in Visual Studio will show us this situation:

It's all about signatures

The screenshot shows the definition of the Where<T> method. Just think about it: the idea is to allow us to filter collection data in a manner similar to how the where clause does in the SQL syntax. What we express in a where clause is a Boolean condition, just like in Mathematical Logic, a predicate is an assertion that is always evaluated to be true or false.

For instance, we can recode the previous scenario using numberList directly, with something like this:

// Method Where in generic lists
numberList = numberList.Where(x => ((x % 3 == 0) || (x % 7 == 0))) .ToList();

Same results are obtained with much less plumbing, so we can focus more on the problem to be solved and less on the algorithm required.

Many more methods were added and immediately accepted by the programmer's community due to the productivity linked to them. For the case with no return values, the body code is supposed to act over something external to the method. In our example, it could be something like adding a number to the list of selected values.

In this way, we can handle more complex situations, such as the case where we need to calculate multiples of two numbers starting with a certain digit, such as in this code:

// We can create a more complex function including
// any number of conditions
Action<int> MultipleConditions = n =>
{
  if ((n % 3 == 0) && (n % 2 == 0))
  {
    if (n.ToString().StartsWith("5")) {
      selectedNumbers.Add(n);
    }
  }
};
numberList.ForEach(MultipleConditions);

In this variation, we use the ForEach method, which receives an Action delegate argument, as we can see in the tooltip definition offered by the IDE's Editor:

It's all about signatures

How do these sentences translate into real code? It might be a bit surprising for the curious reader to take a look at the MSIL code produced by this code. Even a simple lambda expression can become more complex than one might think a priori.

Let's take a look at the syntax of our previous x => x % 3 == 0 lambda expression that we have been using. The trick here is that (internally) this is converted to a tree expression, and if you assign that expression to a variable of type Expression<TDelegate>, the compiler generates the code to build an expression tree representing that lambda expression.

So, consider that we express the lambda in its alternative syntax using an Expression object, such as in this code:

Expression<Func<int, bool>> DivBy3 = x => (x % 3) == 0;

Once compiled, you can check the disassembly code and find the equivalent in the MSIL code, which is made up of several declarations of individual expressions, as shown in the following screenshot (just a fragment of what is inside):

It's all about signatures

This equivalence becomes more evident if we translate the code of one of these expressions into its individual parts. The official MSDN documentation gives us the clue by comparing a simple lambda built using expressions with its generated parts. So, they start by saying something like this:

// Lambda defined as an expression tree.
Expression<Func<int, bool>> xTree = num => num > 3 ;

This is followed by the decomposition of this expression tree:

// Decompose the expression tree.
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
BinaryExpression operation = (BinaryExpression)exprTree.Body;
ParameterExpression left = (ParameterExpression)operation.Left;
ConstantExpression right = (ConstantExpression)operation.Right;
// And print the results, just to check.
Console.WriteLine("Expression: {0} => {1} {2} {3}",
  param.Name, left.Name, operation.NodeType, right.Value);

Well, the result of this decomposition is as follows:

It's all about signatures

This is equivalent to the lambda expression, but now we can see that internally, operating with the individual components of the tree is equivalent to the shorten lambda expression.

The LINQ syntax

The goal of all this, besides making things easier for the way we deal with collections, is to facilitate data management. That means reading information from a source and converting it into a collection of objects of the desired type, thanks to these generic collections.

However, what if I want to express a query in a similar syntax to a SQL query? Or, simply, what if the complexity of the query doesn't make it easy to express it with the generic methods indicated so far?

The solution came in form of a new syntax, inherent to the C# (and other .NET languages), called LINQ (Language-Integrated Query). The official definition presents this extension as a set of features introduced in Visual Studio 2008 that extends powerful query capabilities to the language syntax of C#. Specifically, the authors highlight this feature as something that takes the form of LINQ provider assemblies that enable the use of LINQ with .NET Framework collections, SQL Server databases, ADO.NET Datasets, and XML documents.

So, we are given a new SQL-like syntax to generate any kind of query in such a way that the same sentence structure is valid for very different data sources.

Remember that previously, data queries had to be expressed as strings without type checking at compile time or any kind of IntelliSense support, and it was mandatory to learn a different query language depending on the type of data source: SQL databases, XML documents, Web services, and so on.

LINQ syntax is based on the SQL language

In this case, as Hejlsberg mentioned many times, they had to change the order of the clauses if they wanted to provide any kind of Intellisense, so a query of this type adopts the form of this:

var query = from [element] in [collection]
where [condition1 | condition2 ...]
select [new] [element];

In this way, once the user specifies the source (a collection), Visual Studio is able to provide you with Intellisense for the rest of the sentence. For instance, in order to select a few numbers from a number list, such as the ones used in previous examples, we can write the following:

// generate a few numbers
var numbers = Enumerable.Range(50, 200);
// use of linq to filter
var selected = from n in numbers
  where n % 3 == 0 && n % 7 == 0
  select n;
Console.WriteLine("Numbers divisible by 3 and 7 

");
// Now we use a lambda (Action) to print out results
selected.ToList().ForEach(n => Console.WriteLine("Selected: {0} ", n));

Note that we have used the && operator to concatenate both conditions (we'll go further into this in a bit), and there is no problem with using the LINQ syntax in conjunction with lambda expressions. Furthermore, it is recommended that you express the query in the more suitable, readable, and maintainable way. Of course, the output is still what is expected:

LINQ syntax is based on the SQL language

The only condition required for the collection is that it should support IEnumerable or the generic IEnumerable<T> interfaces (or any other interface that inherits from it).

As you may expect, often, the collection is just a previously obtained collection of business logic objects as the result of a query to a database table.

Deferred execution

However, there is a very important point to remember: the LINQ syntax itself uses a model called deferred execution or lazy loaded. This means that a query is not executed until the first bit of data is required by another sentence.

Anyway, we can force the execution by converting the resulting collection into a concrete collection, for example, by calling the ToList() method or requiring other data linked to the actual use of the collection, such as counting the number of rows returned.

This is something we can do by enclosing the LINQ query in parenthesis and applying the solicited operation (note that the value returned is automatically converted into the appropriate type), as shown in the following screenshot:

Deferred execution

In a similar way, we can order the resulting collection in an ascending (the default) or descending manner using the ad-hoc clause:

var totalNumbers = (from n in numbers
  where n % 3 == 0 && n % 7 == 0
  orderby n descending
  select n).Count();

Joining and grouping collections

In the same way as we mimic the syntax of SQL for other queries, it's perfectly possible to use other advanced features of the SQL language, such as grouping and joining several collections.

For the first case (grouping), the syntax is fairly simple. We just have to indicate the grouping factor using the group / by / into clause in this manner:

string[] words = { "Packt", "Publishing", "Editorial", "Books", "CSharp", "Chapter" };
var wordsGrouped = from w in words
group w by w[0] into groupOfWords
select new { FirstLetter = groupOfWords.Key, Words = groupOfWords };
Console.WriteLine(" List of words grouped by starting letter

");
foreach (var indGroup in wordsGrouped)
{
  Console.WriteLine(" Starting with letter '{0}':", indGroup.FirstLetter);
  foreach (var word in indGroup.Words)
  {
    Console.WriteLine(" " + word);
  }
}

Note that we use a nested loop to print the results (one for the groups of words and another for the words themselves). The previous code generates the following output:

Joining and grouping collections

For the case of joining, we use the join clause together with the equals, as, and is operators to express conditions for the junction.

A simple example could be the joining of two different sets of numbers in the search for common elements of any kind. Every set would express a unique condition and the join would establish the common factor.

For instance, starting with our selected numbers (divisible by 3 and 7), let's add another subset of those that start with 7:

var numbersStartingBy7 = from n in numbers
where n.ToString().StartsWith("7")
select n;

Now, we have two different subsets with different conditions. We can find out which among them fulfills both conditions, expressing the requirement by means of a join between both subsets:

var doubleMultiplesBeg7 = from n in selected
join n7 in numbersStartingBy7
on n equals n7
select n;

We find a total of five numbers that start with 7, both being multiples of 3 and 7, as shown in the following screenshot:

Joining and grouping collections

Type projections

Another option (and a very interesting one) is the capability of projecting the required output to an anonymous type (inexistent), which is the result of a selection or which includes the creation of another calculated field.

We perform this action by creating the anonymous output type in the select declaration of the LINQ query, with the capability of naming the desired results the way we want (just like when we create another anonymous type). For instance, if we need another column indicating the even or odd character of the resulting numbers, we can add the following expression to the previous query like this:

var proj = from n in selected
join n7 in numbersStartingBy7 on n equals n7
select new { Num = n, DivBy2 = (n % 2 == 0) ? "Even" : "Odd" };

What follows the select clause is an anonymous type composed of two fields, Num and DivBy2, using a simple ? operator expression, which checks the integer division by 2, the same way we did it earlier. The results look like what is shown in the following output:

Type projections

Besides auxiliary operations like these, the LINQ syntax is especially useful when dealing with databases. Just think of the source collections as return values obtained by querying a valid data origin, which—in all cases—will implement the IEnumerable and/or IQueryable interfaces, which is what happens when we access a real database engine using Entity framework, for example.

We will cover database access in the upcoming chapters, so just keep in mind this methodology that will be applied when we query real data.

Extension methods

Finally, we can extend existing classes' functionality. This means extending even the .NET Framework base types, such as int or String. This is a very useful feature, and it's performed in the way it is recommended by the documentation; no violation of basic principles of OOP occurs.

The procedure is fairly simple. We need to create a new public static top level (not nested) class containing a public static method with an initial argument declaration especially suited for the compiler to assume that the compiled code will be appended to the actual functionality of the type.

The procedure can be used with any class, either belonging to the .NET framework or a customized user or class.

Once we have the declaration, its usage is fairly simple, as shown in this code:

public static class StringExtension
{
  public static string ExtendedString(this string s)
  {
    return "{{ " + s + " }}";
  }
}

Note that the first argument, referred with the this keyword, references the string to be used; so, in this example, we will call the method without any extra arguments (although we can pass as many arguments as we need for other extensions). To put it to work, we just have to add something like this:

Console.WriteLine("The word " + "evaluate".ExtendedString() + " is extended");

We will get the extended output with the word enclosed in double brackets:

Extension methods
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.251.70