Sunteți pe pagina 1din 12

Introduction

some of the C# 3.0 new language and compiler features ,let's define all the new
features:

1. Implicitly Typed Local Variables and Arrays


2. Object Initializers
3. Collection Initializers
4. Extension Methods
5. Anonymous Types
6. Lambda Expressions
7. Query Keywords
8. Auto-Implemented Properties
9. Partial Method Definitions

Implicitly Typed Local Variables and Arrays

Instead of using the explicit type, now we can use the inferred type which means
declaring any local variable as var and the type will be inferred by the compiler from
the expression on the right side of the initialization statement.
This inferred type could be:

• Built-in type
• Anonymous type (will be discussed later)
• User-defined type
• Type defined in the .NET Framework class library

Now let's see how local variables can be declared with var:

var int_variable = 6; // int_variable is compiled as an int


var string_variable = "Mony"; // string_variable is compiled as a
string
var int_array = new[] { 0, 1, 2 }; // int_array is compiled as int[]

// Query is compiled as IEnumerable


var Query =
from c in customers
where c.Name == "Mony"
select c;

// anonymous_variable is compiled as an anonymous type


var anonymous_variable = new { Name =
var list = new List"Mony", Job = "Web Developer" };

// Implicitly Typed Arrays


var int_array = new[] { 1, 10, 100, 1000 }; // int[]
var string_array = new[] { "hello", null, "world" }; // string[]

Restrictions when using implicitly-typed variables are as follows :

• var can only be used when you are to declare and initialize the local variable
in the same statement.
• The variable cannot be initialized to null.
• var cannot be used on fields at class scope.
• Variables declared by using var cannot be used in the initialization
expression. In other words, var i = i++; produces a compile-time error.
• Multiple implicitly-typed variables cannot be initialized in the same statement.
• If a type named var is in scope, then you will get a compile-time error if you
try to initialize a local variable with the var keyword.

Object Initializers

Sometimes you spend a lot of time writing a lot of redundant code to declare
constructors that do the same job. Object initializers can be used to initialize types
without writing explicit constructors.

Code Example 1
private class Person
{
// Auto-implemented properties
public int Age { get; set; }
public string Name { get; set; }
}

static void Test()


{
// Object initializer
Person per = new Person { Age = 22, Name = "Mony" };
}
Code Example 2
class Point
{
int x, y;
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}

When you instantiate this class, you normally write the following code:

Point p = new Point();


p.X = 10;
p.Y = 20;

Instead, you can create and initialize a Point object like this:

Point p = new Point { X = 10, Y = 20 }; // object initializer

Or even like this:


var p = new Point { X = 10, Y = 20 }; // object initializer

With complex fields, such as a square or a rectangle whose corners are located at
the points p1 and p2, you can create the Rectangle class as follows:

public class Rectangle


{
Point p1;
Point p2;
public Point ULcorner { get { return p1; } set { p1 = value; } }
public Point LRcorner { get { return p2; } set { p2 = value; } }
}

You can create and initialize the Rectangle object like this:

var rectangle = new Rectangle { ULcorner = new Point { X = 0, Y = 0 },


LRcorner = new Point { X = 10, Y = 20 } };

Collection Initializers

Enables initialization of collections with an initialization list rather than specific calls
to Add or another method. This initialization has the same effect as using the Add
method with each collection element.

public class Person


{
string _Name;
List _Intersets = new List();
public string Name { get { return _Name; } set { _Name =value; } }
public List Interests { get { return _Intersets; } }
}

class Test
{
static void Main(string[] args)
{
List PersonList = new List();
Person p1 = new Person();
p1.Name = "Mony Hamza";
p1.Interests.Add("Reading");
p1.Interests.Add("Running");
PersonList.Add(p1);
Person p2 = new Person();
p2.Name = "John Luke";
p2.Interests.Add("Swimming");
PersonList.Add(p2);
}
}

In C# 3.0, you can write less code to express the same concept:

static void Main(string[] args)


{
var PersonList = new List{
new Person{ Name = "Mony Hamza", Interests = { "Reading", "Running" } },
new Person { Name = "John Luke", Interests = { "Swimming"} };
}
Extension Methods

Extension methods enable you to "add" methods to existing types without creating a
new derived type, recompiling, or otherwise modifying the original type.

To create an extension method, declare it as a static method in a static class.


The first parameter of an extension method must be the keyword this.

The following is an example of an extension method to convert the temperature from


Fahrenheit to Celsius.

namespace MyNameSpace
{
public static class MyClass
{
public static double ConvertToCelsius(this double fahrenheit)
{
return ((fahrenheit – 32) / 1.8); }

}
}
}

Now it is possible to invoke the extension method, ConvertToCelsius, as if it is an


instance method:

double fahrenheit = 98.7;


double Celsius = fahrenheit.ConvertToCelsius();

So it adds a method called ConvertToCelisius to an existing type which is


double here.

Anonymous Types

The C# compiler enables you to create a new type at runtime which is not available
at the source code level. It encapsulates a set of read-only properties into a single
object without having to first explicitly define a type. The type of the properties is
inferred by the compiler which can create an anonymous type by using the
properties in an object initializer.

For example, consider the following declaration:

var person = new { Name = "Mony Hamza", SSN = "12345678" };

Here, the compiler automatically creates an anonymous type and infers the types of
the properties from the object initializer. It also creates the private fields
associated with these properties and the necessary set and get accessors. When
the object is instantiated, the properties are set to the values specified in the object
initializer.

Here is an example for declaring an anonymous type and displaying its content:
class MyClass
{
static void Main(string[] args)
{
// Declare an anonymous type:
var obj1 = new { Name = "Mony Hamza", SSN ="12345678" };
// Display the contents:
Console.WriteLine("Name: {0}\nSSN: {1}", obj1.Name,obj1.SSN);
Console.ReadLine();
}
}

Output:

Name: Mony Hamza


SSN: 12345678

I've received a question about why to use "implicitly typed variables". If you are to
use an anonymous variable, then this variable must be initialized using the keyword
var. We also have to consider the following notes about Anonymous types:

• Anonymous types are reference types that derive directly from object. From
the perspective of the common language runtime, an anonymous type is no
different from any other reference types.
• If two or more anonymous types have the same number and type of
properties in the same order, the compiler treats them as the same type and
they share the same compiler-generated type information.
• An anonymous type has method scope. To pass an anonymous type, or a
collection that contains anonymous types, outside a method boundary, you
must first cast the type to object. However, this defeats the strong typing of
the anonymous type. If you must store your query results or pass them
outside the method boundary, consider using an ordinary named struct or
class instead of an anonymous type.
• Anonymous types cannot contain unsafe types as properties.
• Because the Equals and GetHashCode methods on anonymous types are
defined in terms of the Equals and GetHashcode of the properties, two
instances of the same anonymous type are equal only if all their properties
are equal.

Lambda Expressions

Lambda expressions provide a concise syntax for writing anonymous methods that
can contain expressions and statements, and can be used to create delegates. All
lambda expressions use the lambda operator =>, which is read as "goes to". The left
side of the lambda operator specifies the input parameters (if any) and the right side
holds the expression or statement block. The lambda expression x => x * x is
read "x goes to x times x."

Using Anonymous Methods in C# 2.0


public delegate int MyDelegate(int n);
class AnonymouseMethods
{
static void Main()
{
// Anonymous method that returns the argument multiplied by 10:
MyDelegate delegObject1 = new MyDelegate(
delegate(int n) { return n * 10; }
);
// Display the result:
Console.WriteLine("The value is: {0}", delegObject1(5));
}
}
Using Lambda Expressions in C# 3.0
public delegate int MyDelegate(int n);
class LambdaExpresion
{
static void Main()
{
// Anonymous method that returns the argument multiplied by 10:
MyDelegate Obj1= new MyDelegate(
delegate(int n) { return n * 10; }
);
// Display the result:
Console.WriteLine("The value using an anonymous method is: {0}",
Obj1(5));
// Using lambda expression to do the same job:
MyDelegate Obj2 = (int n) => n * 10;
// or:
// MyDelegate Obj2 = n => n * 10;
// Display the result:
Console.WriteLine("The value using a lambda expression is: {0}",
Obj2(5));
Console.ReadLine();
}
}

Output:

The value using an anonymous method is: 50


The value using a lambda expression is: 50

We also can use more than one parameter in a lambda expression:

public delegate int MyDelegate(int m, int n);


MyDelegate myDelegate = (x, y) => x * y;
Console.WriteLine("The product is: {0}", myDelegate(5, 4));
More Examples

The following example shows you how to select the number which when divided by
two has a remainder of 1.

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);

The following examples illustrate how to select the strings which starts with the
letter M:

List < string > Names=new List < string >{"Mony","John","Liza"};


List < string > Filterd=Names.FindAll(name =>name.StartsWith("M"));
foreach(string i in Filterd)
{
Console.WriteLine(i);
}

Query Keywords

Keywords:

1. from clause
2. where clause
3. select clause
4. group clause
5. into
6. orderby clause
7. join clause (Inner join, Group join, Left outer join)
8. let clause

To understand Query expressions well, examples are the perfect choice.

The following example illustrates how to select numbers less than 5 and when
divided by two have a remainder of 0.

static void Main()


{
// A simple data source.
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

// Create the query.


// lowNums is an IEnumerable < int >
var lowNums = from num in numbers
where num < 5 && num % 2 == 0

select num;

// Execute the query.


foreach (int i in lowNums)
{
Console.Write(i + " ");
}
}

The following example is from MSDN which will select all the students and their
scores greater than 90, in order to access the inner list of scores. We use compound
from clauses.

Collapse
public class Student
{
public string LastName { get; set; }
public List < int > Scores {get; set;}
}

static void Main()


{
// Use a collection initializer to create the data source. Note that
// each element in the list contains an inner sequence of scores.
List < Student> students = new List < Student>
{
new Student {LastName="Omelchenko", Scores= new List < int> {97, 72,
81, 60}},
new Student {LastName="O'Donnell", Scores= new List < int> {75, 84, 91,
39}},
new Student {LastName="Mortensen", Scores= new List < int> {88, 94, 65,
85}},
new Student {LastName="Garcia", Scores= new List < int> {97, 89, 85,
82}},
new Student {LastName="Beebe", Scores= new List < int> {35, 72, 91, 70}}
};

// Use a compound from to access the inner sequence within each element.
// Note the similarity to a nested foreach statement.
var scoreQuery = from student in students
from score in student.Scores
where score > 90
select new { Last = student.LastName, score };

// Execute the queries.


Console.WriteLine("scoreQuery:");
foreach (var student in scoreQuery)
{
Console.WriteLine("{0} Score: {1}", student.Last, student.score);
}

// Keep the console window open in debug mode.


Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}

Output:

scoreQuery:
Omelchenko Score: 97
O'Donnell Score: 91
Mortensen Score: 94
Garcia Score: 97
Beebe Score: 91

Perform Joins (this example is also from MSDN which shows how to perform
cross join with and without conditions).

Collapse
static void Main()
{
char[] upperCase = { 'A', 'B', 'C'};
char[] lowerCase = { 'x', 'y', 'z'};

var joinQuery1 =
from upper in upperCase
from lower in lowerCase
select new { upper, lower};

var joinQuery2 =
from lower in lowerCase
where lower != 'x'
from upper in upperCase
select new { lower, upper };
// Execute the queries.
Console.WriteLine("Cross join:");
foreach (var pair in joinQuery1)
{
Console.WriteLine("{0} is matched to {1}", pair.upper, pair.lower);
}

Console.WriteLine("Filtered non-equijoin:");
foreach (var pair in joinQuery2)
{
Console.WriteLine("{0} is matched to {1}", pair.lower, pair.upper);
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}

Output:

Cross join:
A is matched to x
A is matched to y
A is matched to z
B is matched to x
B is matched to y
B is matched to z
C is matched to x
C is matched to y
C is matched to z
Filtered non-equijoin:
y is matched to A
y is matched to B
y is matched to C
z is matched to A
z is matched to B
z is matched to C

In the first query expression it selects the Upper, Lower so it matches each upper
letter with all the lower ones. However, in the second filter example, it selects
Lower, Upper so that it matches each lower letter with all the upper ones.

Using Group By

The group clause returns a sequence of IGrouping Of (TKey, TElement))


objects that contain zero or more items that match the key value for the group.

The following example will make it clear:

static void Main()


{
string[] Names = { "Mony", "Hamza", "Marica", "John", "Adam", "Olivia" };
var NameGroups =
from name in Names
group name by name[0];

// Execute the query.


foreach (var name in NameGroups)
{
Console.WriteLine("Names that start with the letter '{0}':", name.Key);
foreach (var name in NameGroups)
{
Console.WriteLine(name);
}
}
}

Output:

Names that start with the letter 'M':


Mony
Marica
Names that start with the letter 'H':
Hamza
Names that start with the letter 'J':
John
Names that start with the letter 'A':
Adam
Names that start with the letter 'O':
Olivia

Now let's add a little modification to the query in the previous example:

var NameGroups =
from name in Names
group name by name[0];
orderby name[0]

The result will be:

Names that start with the letter 'A':


Adam
Names that start with the letter 'H':
Hamza
Names that start with the letter 'J':
John
Names that start with the letter 'M':
Mony
Names that start with the letter 'O':
Olivia
Using into

It can be used to create a temporary identifier to store the results of a group, join
or select clause into a new identifier. This identifier can itself be a generator for
additional query commands.

//MSDN example
static void Main()
{
// Create a data source.
string[] words = { "apples", "blueberries", "oranges", "bananas",
"apricots"};

// Create the query.


var wordGroups1 =
from w in words
group w by w[0] into fruitGroup
where fruitGroup.Count() >= 2
select new { FirstLetter = fruitGroup.Key,
Words = fruitGroup.Count() };

// Execute the query. Note that we only iterate over the groups,
// not the items in each group
foreach (var item in wordGroups1)
{
Console.WriteLine(" {0} has {1} elements.", item.FirstLetter,
item.Words);
}

// Keep the console window open in debug mode


Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}

Output:

a has 2 elements.
b has 2 elements.
Join

Inner Join
Collapse
namespace Joins
{
public class Department
{
public int ID{get; set;}
public string Name{get; set;}
}

public class Employee


{
public int ID{get; set;}
public string Name{get; set;}
public int DeptID{get; set;}
}

public void ManageJoin()


{
List < Department > Departments=new List < Department >
{new Department{ID=1, Name="Sales"},new Department
{ID=2,Name="Marketing"}};
List < Employees > Employees=new List < Employee >
{new Employee {ID=1,Name="Mony",DeptID=1},
new Employee{ID=2,Name="Tom",DeptID=2}};

//Inner Join
var innerJoinQuery = from employee in Employees
join dept in Department on employee.DeptID equals
dept.ID
select new { EmployeeName = employee.Name,
DeptName = dept.Name };
}
}

The previous example will return each employee name and the department name.

Group Join
A join clause with an into expression is called a group join.

Left Outer Join

In a left outer join, all the elements in the left source sequence are returned,
even if no matching elements are in the right sequence. To perform a left outer
join, use the DefaultIfEmpty method in combination with a group join.

Let's modify the previous example to apply Left Outer Join:

var LeftouterJoinQuery = from employee in Employees


join dept in Department on employee.DeptID equals
dept.ID
select new { EmployeeName = employee.Name,
DeptName = dept.Name } into empgroup
select empgroup.DefaultIfEmpty(new
{ EmployeeName = employee.Name, DeptName =
"No Department"});

Now it selects all the employees including those who are not attached to department
yet.

Auto-Implemented Properties

You can use Auto Implemented Properties when no Additional logic is required, but
you have to declare both a get and a set accessor.

//Auto Implemented Properties are used in the previous example, you can check
it.

Partial Method Definitions

A partial class or struct may contain a partial method. One part of the class
contains the signature of the method. An optional implementation may be defined in
the same part or another part. If the implementation is not supplied, then the
method and all calls to the method are removed at compile time.

S-ar putea să vă placă și