New Object-Oriented Features in C# 3

Document technical information

Format doc
Size 432.6 kB
First found May 22, 2018

Document content analysis

Category Also themed
Language
English
Type
not defined
Concepts
no text concepts found

Persons

Organizations

Places

Transcript

1
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.
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
2
public
public
public
public
public
public
public
public
int ProductId { get; set; }
int CatId { get; set; }
string ProductSDesc { get; set; }
string ProductLDesc { get; set; }
string ProductImage { get; set; }
decimal Price { get; set; }
bool InStock { get; set; }
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<int> Scores = new List<int> { 85, 98, 91 };
List<ProductInfoShort> PList = new
List<ProductInfoShort>
{new ProductInfoShort{ProductId=1235,
ProductSDesc="Calculator",Price=24.50m},
3
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<string, List<float>>();
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
4
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.
5
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="XYZEVEDBCONN"
connectionString="server=pico2;uid=sa;pwd=yourpw;database=XYZEVEDB"/>
</connectionStrings>
</configuration>
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"].Connection
String;
public static List<T> GetDBList<T>(string sql)
where T : IReaderData, new()
// T has to implement IReaderData, and be creatable
{
SqlConnection conn = new SqlConnection(connStr);
List<T> cList = null;
try
{
conn.Open();
cList = new List<T>();
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();
}
6
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<ProductInfoShort> PList =
DBList.GetDBList<ProductInfoShort>("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.
7
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.
After you click OK, Visual Studio will show the following information
Click on the “Server Explorer” link and add a new database connection as shown below.
8
My database server is called “pico2”. Specify the following properties to connect to the
database.
9
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”.
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"];
}
10
#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<OrderGen> OList =
DBList.GetDBList<OrderGen>("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 <T>) 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.
11
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)
12
{
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()
13
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<T> 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.
14
//----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.,
Here is a brief list of possible operations with LINQ to Objects.
Operator
Type
Aggregation
Conversion
Element
Equality
Generation
Grouping
Joining
Ordering
Partitioning
Quantifiers
Restriction
Selection
Set
Operator Name
Aggregate, Average, Count, LongCount, Max, Min, Sum
Cast, OfType, ToArray, ToDictionary, ToList, ToLookup, ToSequence
DefaultIfEmpty, ElementAt, ElementAtOrDefault, First, FirstOrDefault,
Last, LastOrDefault, Single, SingleOrDefault
EqualAll
Empty, Range, Repeat
GroupBy
GroupJoin, Join
OrderBy, ThenBy, OrderByDescending, ThenByDescending, Reverse
Skip, SkipWhile, Take, TakeWhile
All, Any, Contains
Where
Select, SelectMany
Concat, Distinct, Except, Intersect, Union
15
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());
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<ProductInfoShort> PList =
DBList.GetDBList<ProductInfoShort>("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.
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.
16
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<UserGen> UList =
DBList.GetDBList<UserGen>("select * from Users");
List<OrderGen> OList =
DBList.GetDBList<OrderGen>("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.
17
Database Synchronization Using LINQ:
Create a new windows application called LinqNWDB. From the server explorer, add a
new connection to XYZEVEDB, as shown below.
18
Add a new item to the project. Choose “LINQ to SQL Classes” as shown below.
19
From the serer explorer, drag and drop the Products table on the designer surface, as
shown below.
×

Report this document