Module 3 Advanced Features Of C30 Computer Science Essay

Published:

Some of these language enhancements and new features include partial methods, collection initializers, lambda expressions, and query expressions. This session explores these enhancements and features and shows with the help of examples how to make use of them.

3.2 Partial Methods

Consider a partial class Shape whose complete definition is spread over two files. Now consider that a method Create() has a signature defined in Shape.

The partial class Shape contains the definition of Create() in Shape.cs. The remaining part of partial class Shape is present in Shape.Designer.cs and it contains the implementation of Create(). Hence, Create() is a partial method whose definition is spread over two files.

A partial method is a method whose signature is included in a partial type, such as a partial class or struct. The method may be optionally implemented in another part of the partial class or type or same part of the class or type.

Lady using a tablet
Lady using a tablet

Professional

Essay Writers

Lady Using Tablet

Get your grade
or your money back

using our Essay Writing Service!

Essay Writing Service

The following codes illustrate how to create and use partial methods. The first file contains only the signature and the second file contains the implementation.

Example 1: Shape.Designer.cs

namespace PartialTest

{

public partial class Shape

{

partial void Create();

}

}

Example 2: Shape.cs

namespace PartialTest

{

public partial class Shape

{

partial void Create()

{

Console.WriteLine("Creating Shape");

}

public void Test()

{

Create();

}

}

class Program

{

static void Main(String[] args)

{

Shape s = new Shape();

s.Test();

}

}

}

By separating the definition and implementation into two files, it is possible that two developers can work on them or even use a code-generator tool to create the definition of the method. Also, it is upto the developer whether to implement the partial method or not.

It is also valid to have both the signature and implementation of Create() in the same part of Shape.

Example 3: Shape.cs

namespace PartialTest

{

public partial class Shape

{

partial void Create();

partial void Create()

{

Console.WriteLine("Creating Shape");

}

public void Test()

{

Create();

}

}

class Program

{

static void Main(String[] args)

{

Shape s = new Shape();

s.Test();

}

}

}

It is possible to have only the signature of Create() in one part of Shape and no implementation of Create() anywhere. In that case, the compiler removes all references to Create(), including any method calls.

A partial method must always include the keyword partial.

Also, partial methods can be defined only within a partial class or type. If the class containing the definition or implementation of a partial method does not have the partial keyword, then a compile-time error would be raised.

There are several restrictions when working with partial methods:

The partial keyword is a must when defining or implementing a partial method

Partial methods must return void

They are implicitly private

Partial methods can return ref but not output

Partial methods cannot have any access modifier such as public, private and so forth or keywords such as virtual, abstract, sealed, or so forth

Partial methods are useful when you have part of the code auto-generated by a tool or IDE and want to customize the other parts of the code.

3.3 Collection Initializers

The .NET Framework and C# language provide a set of specialized classes to store and retrieve data. These classes are called collection classes. Collection classes allow you to implement stacks, lists, and other data structures easily.

In the earlier versions of C#, after an instance of a collection has been created, you normally populated data into the instance using a method such as Add().

Code Snippet 1 shows an instance of the generic List collection can be populated with strings.

Code Snippet 1

// Instantiate a generic List collection of strings

Lady using a tablet
Lady using a tablet

Comprehensive

Writing Services

Lady Using Tablet

Plagiarism-free
Always on Time

Marked to Standard

Order Now

List<string> names = new List<string>();

// Add the elements one by one

names.Add("Hanna");

names.Add("Peter");

names.Add("Jim");

names.Add("Karl");

names.Add("Cathy");

// Iterate through the collection

foreach (string name in names)

{

Console.WriteLine(name);

}

C# 3.0 has introduced a new feature called collection initializers that enable you to initialize instances of collection classes at the time of creating the instances. Using collection initializers, you can create the instance and assign elements to it in a single statement.

Syntax:

CollectionClassName objectName = new CollectionClassName{ collection-initializer }

where,

CollectionClassName: is the name of a collection class

objectName: is the name of the object of the collection class

collection-initializer: value, expression, or element-initializer-list

Code Snippet 2 shows how the earlier code (in Code Snippet 1) can be rewritten using a collection initializer.

Code Snippet 2

// Instantiate and initialize a generic List collection of strings using

// collection initializer

List<string> names = new List<string>() {"Hanna", "Peter", "Jim", "Karl", "Cathy" };

foreach (string name in names)

{

Console.WriteLine(name);

}

The elements used to initialize a collection can be a value, an expression, an object initializer, or even a method call.

The following example makes use of both object initializers and collection initializers.

Example 4

/// <summary>

/// Class Person provides the definition for a person object having name, age, and an /// optional nickname.

/// </summary>

public class Person

{

public string Name { get; set; }

public int Age { get; set; }

public string NickName { get; set; }

}

/// <summary>

/// Class Friends defines a List of Persons and initializes the list with values using

/// both object and collection initializers.

/// </summary>

public class Friends

{

static void Main()

{

List<Person> friends = new List<Person>

{

new Person(){Name = "Gia Smith", Age = 34, NickName = "Gia"},

new Person(){Name = "Tony Parker", Age = 22, NickName = "Tone"},

new Person(){Name = "Jeffrey B", Age = 19, NickName = "Jeff"},

};

foreach (Person friend in friends)

{

Console.WriteLine("{0} {1:G} {2}", friend.Name, friend.Age, friend.NickName);

}

}

}

Figure 3.1 shows the output of the code.

Figure 3.1: Output

Figure 3.2 shows the two types of initializers.

Figure 3.2: Object and Collection Initializers

In Example 5, the same code is written without using object initializers and collection initializers.

Example 5

public class Person

{

public string Name { get; set; }

public int Age { get; set; }

public string NickName { get; set; }

}

public class Friends

{

static void Main()

{

Person person1 = new Person();

person1.Name = "Gia Smith";

person1.Age = 34;

person1.NickName = "Gia";

Person person2 = new Person();

person2.Name = "Tony Parker";

person2.Age = 22;

person2.NickName = "Tone";

Person person3 = new Person();

person3.Name = "Jeffrey Bridges";

person3.Age = 19;

person3.NickName = "Jeff";

List<Person> friends = new List<Person>();

friends.Add(person1 );

friends.Add(person2);

friends.Add(person3);

foreach (Person friend in friends)

{

Console.WriteLine("{0} {1:G} {2}", friend.Name, friend.Age,

friend.NickName);

}

}

}

Both the examples produce the same output but the code shown in Example 1 is more compact and readable.

You can also specify null in the collection initializer if a collection class allows you to add null elements.

For example,

List<string> colors = new List<string> {"blue", "red", null};

Collection initializers can be used with any collection class as long as there is an appropriate Add() method implemented in that collection to add elements.

Lady using a tablet
Lady using a tablet

This Essay is

a Student's Work

Lady Using Tablet

This essay has been submitted by a student. This is not an example of the work written by our professional essay writers.

Examples of our work

For example, you can use them with a generic Dictionary class.

Dictionary<string, int> words = new Dictionary<string, int>{

{"hither",1},

{"gall",2},

{"valour",3}

};

There are some restrictions on collection initializers. They are:

The collection object to which a collection initializer is applied must be of a type that implements System.Collections.Generic.ICollection<T> for exactly one T. In simple terms, this means that if you have a collection of strings as a list, for example, then the list must initialize exactly one list of strings and not more. The following code demonstrates this concept:

List<string> colors = new List<string> { "blue", "red", "orange" },

{"yellow", "green"}; // Error

Here, you are trying to initilaize two lists with one initializer. Hence the code is invalid.

It must be possible to implicitly convert from the type of each element initializer to T. An example of this is shown below:

List<float> nums = new List<float> { 65.0F, 'A', 65 };

The above statement will succeed because int and char values can be implicitly converted to float. However, the below statement will not work.

List<int> nums = new List<int> { 65.0F, 'A', 65 };

This is because there is no implicit conversion from float to int.

If any of the above two restrictions is not satisfied, a compile-time error is displayed.

3.4 System-defined Generic Delegates

As you are already aware, a delegate is a reference to a method. Consider an example to understand this better. You create a delegate CalculateValue to point to a method that takes a string parameter and returns an int. But you need not specify the method name at the time of creating the delegate. At some later stage in your code, you instantiate the delegate by assigning it a method name.

public delegate int CalculateValue(string input);

public class Test

{

public int GetValue(string input)

{

return input.Length;

}

static void Main(String[] args)

{

Test t = new Test();

CalculateValue calc = t.GetValue;

Console.WriteLine(calc("Test"));

}

}

The .NET Framework 3.5 and C# 3.0 have a set of System-defined generic delegates that take a number of parameters of specific types and return values of another type.

These are described here.

Func<TResult>() Delegate

It represents a method having zero parameters and returns a value of type TResult.

Syntax:

public delegate TResult Func<TResult>()

The following example demonstrates how to use this delegate. A filename is accepted through the console input and the code checks to see if a file with that name exists on the local system or not. If the file does not exist, it is created and a line "Testing generic parameterless delegate" is added to the file. If the file already exists, no action takes place.

Example 6

/// <summary>

/// Class FileWriter writes a line of text to a file based on whether it exists or not.

/// </summary>

public class FileWriter

{

string fn = null;

public static void Main()

{

FileWriter fw = new FileWriter();

// Create the generic delegate

Func<bool> methodCall = fw.CheckFile;

// Pass the delegate as parameter to Create

fw.Create(methodCall(), fw.fn);

}

public void Create(bool status, string fn)

{

if (!status)

{

StreamWriter sn = new StreamWriter(fn);

sn.WriteLine("Testing generic parameterless delegates");

sn.Close();

Console.WriteLine("Successfully written text to the file.");

}

}

public bool CheckFile()

{

try

{

// Get the file name from input

fn = Console.ReadLine();

// Check for existence of the file

if (File.Exists(fn))

return true;

else

return false;

}

catch

{

return false;

}

}

}

Here, the delegate methodCall is passed as a parameter to the method, Create(). Since methodCall() is a delegate pointing to the method CheckFile, the results of the method CheckFile() are actually sent as a parameter to Create(). In the CheckFile() method, the filename is read from the input and the built-in Exists() method of File class is invoked on it. If the file exists, a value of true is returned, otherwise false is returned. Within Create(), based on the input parameter, status, either the file is created or no action takes place.

Func<T, TResult>(T arg) Delegate

It represents a method having one parameter of type T and returns a value of type TResult.

Syntax:

public delegate TResult Func<T, TResult>(T arg)

The following example determines the length of a given word or phrase. It makes use of a system-defined generic delegate that takes one parameter and returns a result.

Example 7

/// <summary>

/// Class WordLength determines the length of a given word or phrase

/// </summary>

public class WordLength

{

public static void Main()

{

// Instantiate delegate to reference Count method

Func<string, int> count = Count;

string location = "Netherlands";

// Use delegate instance to call Count method

Console.WriteLine("The number of characters in the input is: {0}

",count(location).ToString());

}

private static int Count(string inputString)

{

return inputString.Length;

}

}

Figure 3.3 shows the output.

Figure 3.3: Output

Func<T1, T2, TResult>(T1 arg1, T2 arg2) Delegate

It represents a method having two parameters of type T1 and T2 respectively and returns a value of type TResult.

Syntax:

public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2)

The following example determines the position of a given word in a phrase. It makes use of a system-defined generic delegate that takes two parameters and returns a result.

Example 8

/// <summary>

/// Class WordPosition determines the position of a given word in a phrase

/// </summary>

public class WordPosition

{

public static void Main()

{

// Instantiate delegate to reference WordPosition method

Func<string, string, int> getPosition = GetWordPosition;

string name = "Dakota is a great place";

string word = "great";

// Use delegate instance to call WordPosition method

Console.WriteLine(getPosition(name, word).ToString());

}

// Includes the logic to break the given phrase into several words and

// then search for the particular word

private static int WordPosition(string inputString, string word)

{

string delimiter = " ";

char [] delimiterArr = delimiter.ToCharArray ();

string[] splitStrings = null;

// Break the given input into several split strings and remove empty

// strings if any

splitStrings = inputString.Split(delimiterArr,

StringSplitOptions.RemoveEmptyEntries);

int i = 0;

foreach (string singleString in splitStrings)

{

if (singleString.Equals(word))

return i+1;

i++;

}

return -1;

}

}

Similarly there are two other Func delegates that take three and four arguments respectively.

The advantage of all these system-defined generic delegates is that they are ready for reuse with minimal coding. The effort of defining such delegates is no longer required since the delegates already exist.

3.5 Lambda Expressions

A method associated with a delegate is never invoked by itself, instead, it is only invoked through the delegate. Creating a separate named method just so that it can be invoked through the delegate can sometimes be very cumbersome. To overcome this, a feature called anonymous function is introduced in C# 3.0.

Anonymous functions allow unnamed blocks of code to be created for representing a method referred by a delegate. In C# 2.0, this was done through the anonymous method feature. C# 3.0 has introduced a powerful and better alternative to anonymous methods in the form of lambda expressions.

A lambda expression is an anonymous expression that can contain expressions and statements and enables to simplify development through inline coding.

In simple terms, a lambda expression is an inline expression or statement block having a compact syntax and can be used wherever a delegate or anonymous method is expected.

Syntax:

parameter-list => expression or statements

where,

parameter-list is an explicitly typed or implicitly typed parameter list

=> is the lambda operator

expression or statements are either an expression or one or more statements

For example,

word => word.Length;

Let us take a complete example to illustrate the use of lambda expressions. Assume that you want to calculate the square of an integer number. You can use a method Square() and pass the method name as a parameter to the Console.WriteLine() method.

You can do this using any one of three approaches: using a named delegate, using an anonymous delegate, and using a lambda expression.

First, you will see how to achieve this using a named delegate and a method.

Example 9

// Defining a delegate

delegate int ProcessNumber(int input);

/// <summary>

/// Class Program determines the square of a number

/// </summary>

public class Program

{

public static void Main()

{

ProcessNumber test = new ProcessNumber(Square);

// Pass the named delegate as a parameter

Console.WriteLine(test(5));

}

public static int Square(int input)

{

return input * input;

}

}

Example 10

Using anonymous delegate

// Defining a delegate

delegate int ProcessNumber(int input);

/// <summary>

/// Class Program determines the square of a number

/// </summary>

class Program

{

public static void Main()

{

// Use anonymous delegate instead of method

ProcessNumber test = new ProcessNumber(delegate(int input)

{

return input*input;

});

Console.WriteLine(test(5));

}

}

Example 11

Using lambda expression

class Program

{

delegate int ProcessNumber(int input);

static void Main(string[] args)

{

ProcessNumber del = input => input * input;

Console.WriteLine(del(5));

}

}

The => operator is pronounced as 'goes to" or "go to" in case of multiple parameters. Here, the expression, input => input * input means, given the value of input, calculate input multiplied by input and return the result.

Example 9, 10, and 11 return the same output as shown in figure 3.4.

Figure 3.4: Output

3.5.1 Expression Lambdas

An expression lambda is a lambda with an expression on the right side. It has the following syntax

(input_parameters) => expression

where,

input_parameters: one or more input parameters, each separated by a

comma

expression: the expression to be evaluated

The input parameters may be implicitly typed or explicitly typed.

When there are two or more input parameters on the left side of the lambda operator, they must be enclosed within parentheses. If there is no parameter at all, a pair of empty parentheses must be used. However, if you have only one input parameter and its type is implicitly known, then the parentheses can be omitted.

Consider the lambda expression,

(str, str1) => str == str1

It means str and str1 go into the comparison expression which compares str with str1. In simple terms, it means that the parameters str and str1 will be passed to the expression str == str1

Here, it is not clear what are the types of str and str1. Hence it is best to explicitly mention their data types:

(string str, string str1)=> str==str1

To use a lambda expression, declare a delegate type which is compatible with the lambda expression. Then, create an instance of the delegate and assign the lambda expression to it. Invoke the delegate instance. This will result in the lambda expression being executed. The value of the expression will be the result returned by the lambda.

Consider an example to see how this works. The code shown earlier in Example 7 can be rewritten as shown in Example 12:

Example 12

/// <summary>

/// Class WordLength determines the length of a given word or phrase

/// </summary>

public class WordLength

{

public static void Main()

{

Func<string, int> count = name=> name.Length;

Console.WriteLine("The number of characters in the input is: {0}",count("Netherlands").ToString());

}

}

In this program, the objective is to determine the length of a given word or phrase. The program makes use of a system-defined generic delegate that takes one parameter of string type and returns a result of int type. An instance, count, of this delegate is created and assigned a lambda expression. The lambda expression specified in this program means given an input name, retrieve the length using the Length property and return the result. Therefore, an integer result, the length of the given string will be returned. The delegate instance, count, is invoked with an input Netherlands.

In Example 7, the code to achieve this same objective is lengthier. Thus, using a lambda expression as shown in Example 12, the code becomes more concise and compact.

Another simple example to demonstrate expression lambdas is shown below:

Example 13

/// <summary>

/// Class ConvertString converts a given string to uppercase

/// </summary>

public class ConvertString

{

delegate string Convert(string s);

public static void Main()

{

// assign a lambda expression to the delegate instance

Convert con = word => word.ToUpper();

// Invoke the delegate in Console.WriteLine with a string

Console.WriteLine(con("abc"));

}

}

In this example, a delegate named Convert is created and instantiated. At the time of instantiation, a lambda expression word => word.ToUpper() is assigned to the delegate instance. The meaning of this lambda expression is that, given an input, word, call the ToUpper() method on it. ToUpper() is a built-in method of String class and converts a given string into uppercase. Now when the delegate instance is passed an input of "abc" and the result is printed, the output would be "ABC".

The output of the program is shown in figure 3.5.

Figure 3.5: Output

An important point to note is that when you assign an expression lambda to a delegate instance, the lambda expression must be compatible with the delegate instance.

Consider the following lines of code:

delegate string Convert(string s);

. . .

Convert con = word => word.Length; // Error

The above code will generate an error because the lambda expression is not compatible with the delegate declaration. The delegate is supposed to have a return value of string but an int is being assigned to it through the lambda expression. This results in an error.

3.5.2 Statement Lambdas

A statement lambda is a lambda with one or more statements. It can include loops, if statements, and so forth.

(input_parameters) => {statement;}

where,

input_parameters: one or more input parameters, each separated by a

comma

statement: a statement body containing one or more statements

Optionally, you can specify a return statement to get the result of a lambda.

(string str, string str1)=> { return (str==str);}

Example 14

/// <summary>

/// Class WordLength determines the length of a given word or phrase

/// </summary>

public class WordLength

{

delegate void GetLength(string s);

public static void Main()

{

// Here, the body of the lambda comprises two entire statements

GetLength len = name => { int n =

name.Length;Console.WriteLine(n.ToString()); };

// Invoke the delegate with a string

len("Missisippi");

}

}

The output of the program is shown in figure 3.6.

Figure 3.6: Output

The following example demonstrates the use of statement lambdas with loops and if statements.

Example 15

/// <summary>

/// Class PrimeTest prints a list of prime numbers upto a given number

/// </summary>

public class PrintPrimes

{

delegate List<int> GetPrime(int s);

public static void Main()

{

GetPrime primes = n =>

{

List<int> nums = new List<int>();

bool flag = true;

for (int i = 1; i <= n; i++)

{

flag = true;

for (int j = 2; j < i; j++)

{

if (i % j == 0)

{

flag = false;

}

}

if (flag)

nums.Add(i);

}

return nums;

};

// Pass a value of 20 so that you get prime numbers

// from 1 to 20

foreach (int primenum in primes(20))

{

Console.WriteLine(primenum);

}

}

}

In this program, the logic to calculate and retrieve a list of prime numbers is encapsulated within a statement lambda expression. An input n is passed to it through the => lambda operator. Within the statement lambda, two for loops and an if statement are used to determine if each number from 1 to n is divisible only by itself and 1, and if true, then it is added to a list. Finally, the complete list of prime numbers is returned to the delegate instance, primes, which has been declared to be of type List<int>. Thus, the return value of the lambda expression and the delegate type are compatible. Then, a foreach loop iterates through each prime number in the list (which is given an input of 20) and displays it.

The output of the program is shown in figure 3.7.

Figure 3.7: Output

The following table shows examples of expression and statement lambdas:

Example

Lambda Type

value => value + 5

Expression Lambda

(int value) => {return value+5;}

Statement Lambda

() =>5

Parameter-less expression lambda

()=>Console.WriteLine()

Parameter-less statement lambda

Table 3.1: Expression and Statement lambdas

Note: Lambda expressions are extremely useful when working with LINQ.

3.5.3 Lambdas with Standard Query Operators

Lambda expressions can also be used with standard query operators.

Some commonly used standard query operators are listed in the table below:

Operator

Description

Sum

Calculates sum of the elements in the expression

Count

Counts the number of elements in the expression

OrderBy

Sorts the elements in the expression

Contains

Determines if a given value is present in the expression

Table 3.2: Commonly Used Standard Query Operators

The following example shows how to use the OrderBy operator with the lambda operator to sort a list of names.

Example 16

/// <summary>

/// Class NameSort sorts a list of names

/// </summary>

public class NameSort

{

public static void Main()

{

// Declare and initialize an array of strings

string [ ] names = {"Hanna", "Jim", "Peter", "Karl", "Abby", "Benjamin"};

foreach (string n in names.OrderBy(name => name))

{

Console.WriteLine(n);

}

}

}

The code sorts the list of names in the string array alphabetically and then displays them one by one. It makes use of the lambda operator along with the standard query operator OrderBy.

The output of the code is shown in figure 3.8.

Figure 3.8: Output

The following code snippet shows another example of using standard query operators with lambda expressions:

delegate bool TestDel(string[ ] n);

public static void Main()

{

// Declare and initialize an array of strings

string[ ] names = {"Hanna", "Jim", "Peter", "Karl", "Abby", "Benjamin"};

TestDel del = name=> names.Contains("Kerl");

Console.WriteLine(del(names).ToString());

}

The code checks to see if the string array of names contains the name Kerl. It makes use of the lambda operator along with the standard query operator Contains.

The output of the code is shown in figure 3.9.

Figure 3.9: Output

3.6 Query Expressions

A query is a set of instructions that retrieves data from a data source. The source may be a database table, an ADO.NET dataset table, an XML file, or even a collection of objects such as a list of strings. A query expression is a query that is written in query syntax using clauses like from, select, and so forth. These clauses are an inherent part of a LINQ query. LINQ is a set of technologies introduced in Visual Studio 2008. It simplifies working with data present in various formats in different data sources. LINQ provides a consistent model to work with such data.

Through LINQ, developers can now work with queries as part of the C# language. Developers can create and use query expressions, which are used to query and transform data from a data source supported by LINQ. Any query expression has to begin with a from clause and end with a select or group clause.

The example shows a simple example of a query expression. Here, a collection of strings representing names is created and then, a query expression is constructed to retrieve only those names that end with "l".

Example 17

class Program

{

static void Main(string[] args)

{

string[] names = { "Hanna", "Jim", "Pearl", "Mel", "Jill", "Peter", "Karl", "Abby", "Benjamin" };

IEnumerable<string> words = from word in names

where word.EndsWith("l")

select word;

foreach (string s in words)

Console.WriteLine(s);

}

}

The output of the code is shown in figure 3.10.

Figure 3.10: Output

Whenever a compiler encounters a query expression, internally it converts it into a method call, using extension methods. So, an expression such as the one shown below (which is used in above code snippet)

IEnumerable<string> words = from word in names

where word.EndsWith("l")

select word;

is converted as

IEnumerable<string> words = names.Where(word =>word.EndsWith("l"));

Though this line is more compact, the SQL way of writing the query expression is more readable and easier to understand.

Some of the commonly used query keywords used in query expressions are listed in the table.

Clause

Description

from

Used to indicate a data source and a range variable

where

Used to filter source elements based on one or more boolean expressions that may be separated by the operators && or ||

select

Used to indicates how the elements in the returned sequence will look like when the query is executed

group

Used to group query results based on a specified key value

into

Used to indicate an identifier which serves as a reference to the results of a join, select, or group clause

orderby

Used to sort query results in ascending or descending order

ascending

Used in an orderby clause to represent ascending order of sort

descending

Used in an orderby clause to represent descending order of sort

join

Used to join two data sources based on matching criteria

let

Used to introduce a range variable to store sub-expression results in a query expression

by

Used in a group clause

Table 3.3: Query Keywords Used in Query Expressions

A complex example using group clause in a query expression is shown here. Here, a list of products is stored as instances of Product class. Some of the products may have brand name in common. The objective of this program is to group the products based on brand name and display them.

Example 18

/// <summary>

/// Class GroupDemo demonstrates grouping using query expressions

/// </summary>

class GroupDemo

{

public class Product

{

public string Name { get; set; }

public string Brand { get; set; }

public int ID { get; set; }

}

static void Main()

{

// Create the data source.

List<Product> Products = new List<Product>

{

new Product {Name = "Ballpoint Pen", Brand = "Kores", ID = 111},

new Product {Name = "Notepad", Brand = "Kores", ID =112},

new Product {Name = "Marker", Brand = "Mortensen", ID = 113},

new Product {Name = "Gel Pen", Brand = "Mortensen", ID = 114},

new Product {Name = "Paper Knife", Brand = "Garcia", ID = 115}

};

// Group by brand using a query expression.

var stringGroupQuery =

from p in Products

group p by p.Brand;

// Execute the query and access items in each group

foreach (var ProductGroup in stringGroupQuery)

{

Console.WriteLine("Brand : " +ProductGroup.Key);

foreach (var p in ProductGroup)

{

Console.WriteLine(" {0}", p.Name);

}

Console.WriteLine("\n");

}

}

}

Figure 3.11 shows the output.

Figure 3.11: Output

Summary

A partial method is a method whose signature is included in a partial type, such as a partial class or struct.

Partial methods can be defined only within a partial class or type.

Collection initializers enable you to initialize instances of collection classes at the time of creating the instances.

System-defined generic delegates take a number of parameters of specific types and return values of another type.

A lambda expression is an inline expression or statement block having a compact syntax and can be used in place of a delegate or anonymous method.

A query expression is a query that is written in query syntax using clauses like from, select, and so forth.

Check Your Progress

1. Which of the following statements about expression lambdas are true?

In an expression lambda, the parentheses can be omitted if there are two or more input parameters.

An expression lambda cannot include a loop statement within it.

The return type of the expression lambda is the return value of the lambda expression.

It is mandatory that at least one input parameter must be present in an expression lambda.

The parameters in an expression lambda can be implicitly typed or explicitly typed.

2. Which one of the following codes will create and initialize a Hashtable instance named hashFileType using a collection initializer?

(A)

Hashtable hashFileType = new Hashtable{{"txt"}, {"notepad.exe"}, {"bmp"}, {"paint.exe"}};

(B)

Hashtable hashFileType = new Hashtable{{"txt", "notepad.exe"}, {"bmp", "paint.exe"}};

(C)

Hashtable hashFileType = new Hashtable{"txt", "notepad.exe", "bmp", "paint.exe"};

(D)

Hashtable hashFileType = new Hashtable();

hashFileType.Add("txt", "notepad.exe");

hashFileType.Add("bmp", "paint.exe");

3. Which one of the following delegate syntax corresponds to the statement given here?

It represents a method having two parameters of type T1 and T2 respectively and returns a value of type TResult.

(A)

public delegate TResult Func<T1, TResult>(T1 arg1, T2 arg2)

(B)

public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2)

(C)

public delegate TResult Func<T1, T2>(T1 arg1, T2 arg2)

(D)

public delegate Func<T1, T2, TResult>(T1 arg1, T2 arg2)

4. Can you match the descriptions given on the right against the clauses given on the left?

Clause

Description

where

Used to indicate how the elements in the returned sequence will look like when the query is executed

select

Used to filter source elements based on one or more Boolean expressions that may be separated by the operators && or ||

into

Used to introduce a range variable to store sub-expression results in a query expression

let

Used to provide an identifier that can serve as a reference to the results of a join, group or select clause

5. In the snippet given below, can you fill in the blank with the right code involving a lambda expression to produce the output HELLO WORLD?

class Program

{

delegate string Convert(string input);

static void Main(string[] args)

{

Convert del =_____________________

Console.WriteLine(del("Hello World));

}

}