New Object-Oriented Features in C# 3



New Object-Oriented Features in C# 3.0

Automatic Properties:

Automatic properties use an abbreviated syntax for declaring properties e.g., instead of declaring a private data called customerID and the corresponding public property CustomerID as:

private int customerID;

public int CustomerID

{

get { return customerID; }

set { customerID = value; }

}

We can declare,

public int CustomerID { get; set; } // automatic property

The compiler will generate the same code as the full public property and the corresponding private data customerID. The limitation in an automatic property is that you cannot write your additional code in the get or the set part. The automatic properties become very handy when creating classes corresponding to database tables.

Exercise: Suppose there is a table called Products in a database called XYZEVEDB with the following design.

[pic]

Create a Products class using automatic properties to map it to the above table.

Solution: Create a Windows application in VS 2008. Name the project NewOOFeatures. Add a class to the project called “Product.cs” with the following code.

namespace NewOOFeatures

{

class Product

{

// automatic properties

public int ProductId { get; set; }

public int CatId { get; set; }

public string ProductSDesc { get; set; }

public string ProductLDesc { get; set; }

public string ProductImage { get; set; }

public decimal Price { get; set; }

public bool InStock { get; set; }

public int Inventory { get; set; }

}

}

Initializers:

Initializers provide an easy way to initialize an object when a multi parameter constructor is not available e.g., in the previous example of Product class, if one wanted to initialize an object of the Product class, they will need to write the following code.

Product pr = new Product();

pr.ProductId = 1234;

pr.CatId = 10;

pr.ProductSDesc = "Calculator";

pr.ProductImage = "calcss.jpg";

pr.ProductLDesc = "Sharp Solar Powered Scientific Calculator";

pr.Price = 21.50m;

pr.InStock = true;

pr.Inventory = 15;

Alternatively, one could write a single statement using the initializer capability as:

//---------object creation with initializer capability

Product pr2 = new Product

{

ProductId = 1234, CatId = 10, ProductSDesc = "Calculator", ProductImage = "calcss.jpg",

ProductLDesc = "Sharp Solar Powered Scientific Calculator",

Price = 21.50m, InStock = true, Inventory = 15 };

As you can see the initializer capability allows us to initialize an object with many parameters as if a corresponding constructor was available.

Similarly Collection initializers can initialize a collection like an array e.g.,

//----------Collection Initializer-------

List Scores = new List { 85, 98, 91 };

List PList = new List

{new ProductInfoShort{ProductId=1235,

ProductSDesc="Calculator",Price=24.50m},

new ProductInfoShort{ProductId=1235,

ProductSDesc="Calculator",Price=24.50m}};

MessageBox.Show("PList Count = " + PList.Count.ToString());

The above code assumes that there is a class called ProductInfoShort that has three properties i.e., ProductId, ProductSDesc, and Price in it.

Type Inference:

If a local variable need to created and initialized at the same time (a good OOP practice), then type inference allows us to not declare the data type of the variable e.g.,

instead of declaring

string lname = “Baker”;

one could declare as

var lname = “Baker”;

or

var id = 1234;

var dict = new Dictionary();

As long as the initialization value clearly indicates the data type of the variable to be created, the compiler will created the appropriate type for that variable.

Anonymous Types:

Anonymous types allow us to create a type with very few lines of code and instantiate an object as well. This capability is usally combined with an initializer as shown below.

var ProductInfo = new

{

ProductId = 1235,

ProductSDesc = "Laptop",

Price = 599m

};

MessageBox.Show(ProductInfo.Price.ToString());

The anonymous types are usually useful when one needs to create an object representing a few columns out of a database table, as demonstrated by the above example.

Generics and Databases:

Suppose we wanted to create a generic method that returns a List of a set of columns from a database table. Since the columns can be of any data type, the generic List of those columns type will be very useful. To explain this idea, let us try to create a generic method that will return a List of ProductId, ProductSDesc, and Price from the Products table. We want to write this method such that it can be easily adapted to obtain lists from other database tables.

Add an interface called IReaderData to the project with the following code in it.

using System.Data.SqlClient;

namespace NewOOFeatures

{

interface IReaderData

{

void PopulateFields(SqlDataReader dr);

}

}

Add a class called ProductInfoShort that implements the IReaderData interface with the following code in it.

using System.Data.SqlClient;

namespace NewOOFeatures

{

class ProductInfoShort : IReaderData

{

public int ProductId { get; set; }

public string ProductSDesc { get; set; }

public decimal Price { get; set; }

#region IReaderData Members

public void PopulateFields(SqlDataReader dr)

{

ProductId = (int)dr["ProductId"];

ProductSDesc = (string)dr["ProductSDesc"];

Price = (decimal) dr["Price"];

}

#endregion

}

}

As you can see from the above description, that the ProductInfoShort defines the properties corresponding to the three columns that we need from the database table Products. It also implements the IReaderData interface so that the data can be obtained from the data reader and populated in the three properties of this class.

Add an application configuration file to the project with the following code in it.

Add a class called DBList to the project with the following code in it. The most interesting code in this class is a static and generic method called GetDBList that returns a generic List of any type T that implements IReaderData and is creatable.

using System.Configuration;

using System.Data.SqlClient;

namespace NewOOFeatures

{

class DBList

{

public static string connStr = ConfigurationManager.ConnectionStrings["XYZEVEDBCONN"].ConnectionString;

public static List GetDBList(string sql)

where T : IReaderData, new()

// T has to implement IReaderData, and be creatable

{

SqlConnection conn = new SqlConnection(connStr);

List cList = null;

try

{

conn.Open();

cList = new List();

SqlCommand cmd = new SqlCommand(sql, conn);

SqlDataReader dr = cmd.ExecuteReader();

while (dr.Read())

{

T row = new T();

row.PopulateFields(dr);

cList.Add(row);

}

}

catch (Exception ex)

{

throw ex;

}

finally

{

conn.Close();

}

return cList;

}

}

}

Having developed the necessary infrastructure to obtain generic List from a database query, now we can test it by adding a button to the form and writing a simple test code as shown below.

private void btnTestGenericDB_Click(object sender, EventArgs e)

{

List PList =

DBList.GetDBList("select * from Products");

MessageBox.Show("Products obtained = " + PList.Count.ToString());

MessageBox.Show("First Product = " +

PList[0].ProductId.ToString() + ":" + PList[0].ProductSDesc + " " +

PList[0].Price.ToString());

}

Run the program and test the invocation of the GetDBList generic function by clicking on the “Test Generic DB” button. Your output will indicate the number of products obtained and the info about the first product from the database.

Exercise: Add a capability to return a generic List that will provide information about the columns in the Orders table. The Orders table in XYZEVEDB is shown below.

[pic]

[pic]

Solution: An easy solution is to create a class similar to ProductInfoShort. This class will have the five properties corresponding to the five columns from the Orders table and will also implement the IReaderData interface.

A solution that requires even less work is to have the Order class be automatically created for us using the “Linq To SQL Classes” capability in VS 2008. After the Order class has been created fro us, then we can further create our own Order class, perhaps we can call it OrderGen which we can derive from the automatically created Order class and have it implement the IReaderData interface.

First add a new item to the project of type “LINQ To SQL Classes” as shown below. Give it a name of DBXYZEVEClasses.dbml.

[pic]

After you click OK, Visual Studio will show the following information

[pic]

Click on the “Server Explorer” link and add a new database connection as shown below.

[pic]

My database server is called “pico2”. Specify the following properties to connect to the database.

[pic]

After you have added the database connection to the XYZEVEDB database, expand on the server explorer and drag and drop the Orders table on the designer surface (see picture below). This will end up creating a class called “Order” with the five properties corresponding to the five columns in the Orders table. The code for this class is generated in the file called “DBXYZEVEClasses.designer.cs”.

[pic]

Add a class to the project called “OrderGen” with the following code in it.

using System.Data.SqlClient;

namespace NewOOFeatures

{

class OrderGen : Order, IReaderData

{

#region IReaderData Members

public void PopulateFields(SqlDataReader dr)

{

this.OrderNo = (int)dr["OrderNo"];

this.OrderDate = (string)dr["OrderDate"];

this.UserID = (int)dr["UserID"];

this.TotalQty = (int)dr["TotalQty"];

this.TotalCost = (decimal)dr["TotalCost"];

}

#endregion

}

}

Add a button the form called “btnGetOrders” with a text property of “Get Orders List”. Type the following code in it to test obtaining the generic list of Orders from the database.

private void btnGetOrders_Click(object sender, EventArgs e)

{

List OList = DBList.GetDBList("select * from Orders");

MessageBox.Show(OList.Count.ToString());

}

As you can see from the above test code, the same generic method called “GetDBList” is used to operate on the OrderGen class. Also most of the code in the OrderGen class was automatically generated by the “LINQ To SQL Classes” component.

Nullable Value Types ?:

Nullable value types (denoted by a question mark, e.g. int? a = null;) add null to the set of allowed values for any value type. This provides improved interaction with SQL databases, where for example a column of type int can have a possibly null value. Thus a SQL INTEGER NULL column type maps directly to the C# int?.

Note that when a nullable type is boxed that has a value of null e.g.,

int? i = null;

object o = i;

The comparison if (o= = null) will be true, even though internally a nullable type is a generic struct (Nullable ) with a HasValue property set to false.

Coalesce operator ??

The operator ?? returns the first of its operands which is not null (or null, if no such operand exists) e.g.,

object nullObj = null;

object obj = new Object();

return nullObj ?? obj; // returns obj

The primary use of this operator is to assign a nullable type to a non-nullable type with an easy syntax:

int? i = null;

int j = i ?? 0; // if i is not null, initialize j to I, else initialize j to 0.

Extension Methods:

Extension methods allow a class to be extended by a new method. For example, if we wanted to add a method to the class called ProductInfoShort, we can define a new class with a method PInfo as shown below. Note that the “this” keyword is used in the parameter list to indicate the extension of the class next to it.

Add a new class to the project called ExtendProductInfoShort as shown below.

namespace NewOOFeatures

{

static class ExtendProductInfoShort

{

public static string PInfo(this ProductInfoShort ps)

{

return "PID=" + ps.ProductId.ToString() +

" SDesc=" + ps.ProductSDesc + " Price=" + ps.Price.ToString();

}

}

}

The test code for the above extension is shown below.

private void btnTestExtensionMethod_Click(object sender, EventArgs e)

{

ProductInfoShort pi = new ProductInfoShort

{

ProductId = 1236,

ProductSDesc = "Dell Laptop",

Price = 585m

};

MessageBox.Show(pi.PInfo());

}

Anonymous Methods:

Anonymous methods use the delegate capability and can be used in defining event handlers. However they are completely general and apply to anonymous functions. One of the goals of anonymous methods is similar to inline functions in C++ .

Example:

btnTest.Click += new EventHandler(btnTest_Click);

where the btnTest_Click event handler is written as:

void btnTest_Click(object sender, EventArgs e)

{

MessageBox.Show(“Test button Clicked”);

}

Using anonymous methods, the above event handling can be specified as:

btnTestClick += delegate(object sender, EventArgs e)

{

MessageBox.Show(“Test button clicked”);

}

Now the event handler function is anonymous.

Example - Creating a thread that executes an anonymous method.

void StartThread()

{

System.Threading.Thread t1 = new System.Threading.Thread

(delegate()

{

System.Console.Write("Hello, ");

System.Console.WriteLine("World!");

});

t1.Start();

}

Another example – obtaining a reference to an anonymous method:

delegate int MyDel(int a, int b);

…..

MyDel dd = delegate(int x, int y) {

x = x + 1; y = y + 5; return x * y + 6; };

MessageBox.Show(dd(5, 7).ToString());

Lambda Expressions:

Lamda expressions go beyond anonymous types, and further simplify the declaration of functions to a semantic minimum e.g., the previous example of button click handling could be written using lambda expression as:

btnTest.Click += (object sender, EventArgs e)=> MessageBox.Show(“Test button clicked”);

The left side of => operator in a lambda expression indicates the parameter list. Paranthesis around the parameter list are optional if there is only one parameter e.g.,

x => x * x + 3;

Specify zero input parameters with empty parentheses:

() => SomeMethod()

Example:

delegate int Del(int i);

Del d1 = x => x * x + 3;

int res = d1(5); //res = 28

Example:

delegate int MyDel(int a, int b);

MyDel d1 = (x, y) => { x = x + 1; y = y + 5; return x * y + 6; };

MessageBox.Show(d1(5, 4).ToString());

LINQ – Language Integrated Query:

LINQ to Objects:

LINQ to Objects allows for writing SQL like “queries” over collections of objects. There is a large set of prebuilt query operators and we can add our own. LINQ allows you to write queries that filter a list or calculate aggregate functions on elements in a collection as a set.

LINQ can work on any collection type that implements an interface called IEnumerable (and also a new interface called IQueryable). This is almost any collection type built into the .NET class libraries including simple arrays like string[], or int[], and any List collection we define.

LINQ can be issued using a query language type of syntax (similar to SQL), or a method syntax based on lambda expressions.

Example: Add a button to the form with an ID of btnLINQToObjects. Type the following code in the button handler.

private void btnLINQToObjects_Click(object sender, EventArgs e)

{

int[] scores = new int[] { 90, 84, 82, 78, 93, 88, 73, 71 };

var results = from n in scores

where n < 85

orderby n

select n;

string out1 = "";

foreach (int data in results)

out1 += data + "\n";

MessageBox.Show(out1);

}

The same query can be written using lambda expressions as shown below.

//----query using method syntax based on lambda expressions

var results2 = scores.Where(sc => sc < 85).OrderBy(sc=>sc).Select(sc=>sc);

string out2 = "";

foreach (int data in results2)

out2 += data + "\n";

MessageBox.Show(out2);

Changing the order in the method based LINQ technique is OK, i.e., the above query could have been written as:

var results2 = scores.Where(sc => sc < 85).Select(sc => sc).OrderBy(sc => sc);

producing the same result as before i.e.,

[pic]

Here is a brief list of possible operations with LINQ to Objects.

|Operator Type |Operator Name |

|Aggregation |Aggregate, Average, Count, LongCount, Max, Min, Sum |

|Conversion |Cast, OfType, ToArray, ToDictionary, ToList, ToLookup, ToSequence |

|Element |DefaultIfEmpty, ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, |

| |SingleOrDefault |

|Equality |EqualAll |

|Generation |Empty, Range, Repeat |

|Grouping |GroupBy |

|Joining |GroupJoin, Join |

|Ordering |OrderBy, ThenBy, OrderByDescending, ThenByDescending, Reverse |

|Partitioning |Skip, SkipWhile, Take, TakeWhile |

|Quantifiers |All, Any, Contains |

|Restriction |Where |

|Selection |Select, SelectMany |

|Set |Concat, Distinct, Except, Intersect, Union |

As an example, we can apply an aggregate average to find out the average test score as:

var avg = scores.Average();

MessageBox.Show(avg.ToString());

[pic]

If we wanted to find out list of products that cost less than 100 from a collection, then LINQ to Objects can be applied on the list as shown below.

//---------LINQ operations on a List of objects

List PList =

DBList.GetDBList("select * from Products");

// obtain products that cost less than 100

var PList100 = from p in PList

where p.Price < 100

select p;

MessageBox.Show("Products costing < 100 = " +

PList100.Count().ToString());

Joins in LINQ to Objects: We will create an example of joining the Orders and the Users table. Their designs are shown below.

[pic]

[pic]

Double click on the DBXYZEVEClasses.dbml and then drag and drop the Users table from the Server Explorer to the designer surface. This will end up creating the User class corresponding to the Users table. Now add a class to the project called UserGen with the following code in it.

namespace NewOOFeatures

{

class UserGen : User, IReaderData

{

#region IReaderData Members

public void PopulateFields(System.Data.SqlClient.SqlDataReader dr)

{

this.UserID = (int)dr["UserID"];

this.Username = (string)dr["Username"];

this.Password = (string)dr["Password"];

this.PHint = (string)dr["PHint"];

this.PAns = (string)dr["PAns"];

}

#endregion

}

}

Add a button to the form with an name of btnLINQJoin and a text property of “LINQ Join”. Also add a data grid view control to the form with a name of “dgv1”. Type the following code in the btnLINQJoin event handler.

private void btnLINQJoin_Click(object sender, EventArgs e)

{

// joins on lists using LINQ

List UList = DBList.GetDBList("select * from Users");

List OList = DBList.GetDBList("select * from Orders");

// find username of each order

var Orders = from o in OList

join u in UList on o.UserID equals u.UserID

select new { u.Username, o.OrderNo, o.OrderDate, o.TotalQty };

MessageBox.Show(Orders.Count().ToString());

dgv1.DataSource = Orders.ToList();

dgv1.Refresh();

}

Run the program and click on the button. The data grid view output from the LINQ join appears as.

[pic]

Database Synchronization Using LINQ:

Create a new windows application called LinqNWDB. From the server explorer, add a new connection to XYZEVEDB, as shown below.

[pic]

[pic]

Add a new item to the project. Choose “LINQ to SQL Classes” as shown below.

[pic]

From the serer explorer, drag and drop the Products table on the designer surface, as shown below.

[pic]

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download