In this article I have explained the new features being integrated with the language c# 3.0.
The following new features of C# 3.0
-
Implicitly typed local variables
-
Anonymous types
-
Extension methods
-
Object and collection initializers
-
Lambda expressions
-
Query expressions
-
Expression Trees
Implicitly Typed Local Variables:
C# 3.0 introduces a new keyword "var" that allows us to declare a new variable without specifying its data type. The type of variable will be determined based on the value assigned to it. The var keyword is not like "variant" and, also, doesn't mean that the variable is loosely typed, or late-bound. When the complier sees the var keyword it assigns the most appropriate type to that variable.
Examples:
var n=1;
Here the value 1 is an integer, so the above line initializes the value 1 to the variable n, whose type is integer.
var name= "XXX";
Here the value “XXX” is a string value, so the above line initializes the value “XXX” to the variable name, whose type is string.
var strArray = new[] {“red”, “green”, “blue”, “black” };
The above line of code declares the variable strArray as string[].
There are some conditions applied while creating implicitly typed local variables,
-
The initializer cannot be a null value. If it’s null then it throws an exception.
-
The assignment of value must be in the same line of declaration.
-
The initialzer value must be an expression value. Object and collections are not allowed.
-
Multiple implicitly typed variables cannot be initialized in the same statement.
-
The implicitly typed local variable is not used for initializing other variables.
Example:
int i = (i = 20); //This is correct expression
var i = (i = 20); //This expression produces a compile-time error.
-
In most cases the usage of var is optional. When a variable is initialized with an anonymous type we must declare the variable as var if we need to access the properties of the object at a later point.
Anonymous types
Anonymous types give you the flexibility to encapsulate a set of read only properties into a single object, without the need to explicitly define the type first.
The name of anonymous type is generated by the compiler and is not available at the source code level.
Example
The following example shows an anonymous type being initialized with two properties called Total and Status
new { Total = 1000, Status = "Rejected" };
While executing the above line of code, the C# compiler will create a class that looks like as follows:
class __Anonymous1
{
private int _Total = 1000;
private string _Status = "Rejected";
public int Total {get { return _Total; } set { _Total = value; }}
public string Status {get { return _Status; } set { _Status = value; }}
}
If there is one more anonymous type that specifies the same sequence of names and types like above example, the complier will create a single anonymous type for both instances to use. The instances are exchanged because the instances are of the same class and the types are really same.
If we need to create an instance of the above class, we can do that by using the “var” keyword:
var n = new { Total = 1000, Status = "Rejected" };
Here the var keyword, which is explained in the first point, is used.
Anonymous types are class types. Anonymous types consist of one or more than one public read only properties. Methods and events are not allowed.
Extension methods
Extension methods enable you to extend the existing types with additional static methods. We don't need to create a new derived type, recompiling or modifying the original type. Instead we can just extend the type.
Extension methods are defined as static methods. Extension method’s first parameter is preceded by this modifier. Extension method can be used to extend a class or interface, but not to override them.
Example
public static int ToInt32(this string str)
{
return Convert.ToInt32(str) ;
}
The above code will be compiled and executed like as follows,
string str = "1";
int n = str.ToInt32();
Object and collection initializers
Object initializer allows us to assign values to the accessible fields or properties of an object at creation time without having to invoke a constructor.
Example
public class Bank
{
public int Deposit;
public int Withdraw;
}
We can initialize a Bank object using an object initializer like this:
var myAcc = new Bank{ Deposit = 1200, Withdraw= 200} ;
Collection initializers allow us to specify one or more element intializers when we initialize a collection class that implements IEnumerable. By using a collection initializer we don't have to specify multiple calls to the Add method of the class in the source code. The compiler adds the calls for us. The element initializer can be a simple value, an object initializer or an expression.
Example
Consider the following code in C#,
List colors = new List(); colors.Add("red"); colors.Add("green"); colors.Add("blue"); colors.Add("pink"); colors.Add("black");
Now it can be,
List colors = new List {"red", "green", "blue", "pink", "black" } ;
Lambda expressions
The Lambda expression is an anonymous function; it contains the set of expressions and statements. Lambda expressions can be used to create delegates or expression tree types. Lambda expressions are compiled as code or data depending on the context where they are used. All lambda expressions use the operator =>. This operator can be read as "goes to". The left side of the “=>” operator specifies the input parameter (if any) and the right side is expression or statement block.
Lambdas can refer to outer variables that are in scope in the enclosing method or type in which the lambda is defined. Note that an outer variable must have an assignment before it can be consumed in a lambda expression.
Example
n => n * n
The above example can be read as "n goes to n times n".
Consider the delegate type example,
delegate int ExampleDelegate(int j);
static void Main(string[] args)
{
ExampleDelegate chkDelegate = n => n * n;
int sumval = chkDelegate(10); //sumval = 100
}
The result of sumval is 100.
General rules,
-
The lambda expression must have the same number of parameters as the delegate type.
-
Input parameters of the lambda expression must be implicitly convertible to its corresponding delegate parameter.
-
The lambda expression's return value (if exists) must be implicitly convertible to the delegate's return type.
Query expressions
This feature is known as Language Integrated Query (LINQ).A query is a set of instructions that describes what data to retrieve from a given data source and what shape and organization the returned data should have. A query is distinct from the results that it produces. Query expressions can be used to query the data from any LINQ enabled data source. Query expression variables are all strongly typed.
Example
Lets assume we have the following class:
public class Student
{
public int SNo;
public string Name;
public string Grade;
}
Using collection and object initializers we can create the following list of students:
List listStudents = new List { { SNo = 1, Name="xxx", Grade="I" }, { SNo = 2, Name="yyy", Grade ="V" }, { SNo = 3, Name="zzz", Grade ="II" } };
And then we can query the list of Students using query expression:
var tmpquery =
from a in listStudents
where a.Grade == "II"
select new { a.Name,a.Grade };
The “var” keyword used above shows the use of Implicitly Typed Local Variables for variable assignment and new { a.Name,a.Grade } is an example of Anonymous Types so we don't need to declare the new type.
Query expression gets translated using Query Expression Translation,
var tmpquery = listStudents
.Where(a => a.Grade.Equals("II"))
.Select(a => new {a.Name, a.Grade});
Using Lambda expressions it translates to this,
a => a.Grade.Equals("II")
We have already another article about LINQ in detail:
http://www.sql-programmers.com/Blog/tabid/153/EntryId/25/Introduction-to-LINQ.aspx
Expression Trees
Expression trees represent the code in a tree like data structure where each node of a tree is an expression, for example, a method call or a binary operation such as i < j. Then we can compile and run the code represented by expression trees.
When a variable of type Expression is assigned to lambda expression, the compiler won’t translate it into executable code, instead it generates an in-memory tree of objects that represents the structure of the expression itself. These data structures are known as expression trees in C# 3.0.
Example:
Let’s assume we assign a lambda expression to a variable of a delegate type like as follows
Func
The above line of code will be equivalent to the following anonymous method assignment,
Func
As continuation of the above example, we can use the lambda expression to calculate the area of a circle like this:
static Expression
This will be something like the following sequence of assignments:
static ParameterExpression parm =
Expression.Parameter(typeof(double), "radiusval");
static Expression