380 likes | 442 Views
C# 3.0 and .NET 3.5: A Brief Overview. Pavel Je žek. C# 3.0 Features. Implicitly Typed Local Variables Extension Methods Lambda Expressions Object Initializers Collection Initializers Anonymous Types Expression Trees Query Expressions
E N D
C# 3.0 and .NET 3.5: A Brief Overview Pavel Ježek
C# 3.0 Features • Implicitly Typed Local Variables • Extension Methods • Lambda Expressions • Object Initializers • Collection Initializers • Anonymous Types • Expression Trees • Query Expressions • C# 3.0 compiler and .NET 3.5 libraries are part of Visual Studio 2008 Pavel JežekC# 3.0 and .NET 3.5
Implicitly Typed Local Variables Examples: var i = 5;var s = "Hello";var d = 1.0;var numbers = new int[] {1, 2, 3};var orders = new Dictionary<int,Order>(); Are equivalent to: int i = 5;string s = "Hello";double d = 1.0;int[] numbers = new int[] {1, 2, 3};Dictionary<int,Order> orders = new Dictionary<int,Order>(); Errors: var x; // Error, no initializer to infer type fromvar y = {1, 2, 3}; // Error, collection initializer not permittedvar z = null; // Error, null type not permitted Pavel JežekC# 3.0 and .NET 3.5
Extension Methods • Declaration: public static partial class Extensions { public static int ToInt32(this string s) { return Int32.Parse(s); } } • Usage: string s = "1234";int i = s.ToInt32(); // Same as Extensions.ToInt32(s) • Instance methods take precedence over extension methods • Extension methods imported in inner namespace declarations take precedence over extension methods imported in outer namespace declarations Pavel JežekC# 3.0 and .NET 3.5
Extension Methods (2) • Declaration: public static partial class Extensions { public static T[] Slice<T>(this T[] source, int index, int count) { if (index < 0 || count < 0 || source.Length – index < count) throw new ArgumentException(); T[] result = new T[count]; Array.Copy(source, index, result, 0, count); return result; }} • Usage: int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};int[] a = digits.Slice(4, 3); // Same as Extensions.Slice(digits, 4, 3) Pavel JežekC# 3.0 and .NET 3.5
Lambda Expressions • Example of C# 2.0 anonymous method: class Program{ delegate T BinaryOp<T>(T x, T y); static void Method<T>(BinaryOp<T> op, T p1, T p2) { Console.WriteLine(op(p1, p2)); } static void Main(){ Method(delegate(int a, int b) { return a + b; }, 1, 2); } } • C# 3.0 lambda expressions provide further simplification: class Program{ delegate T BinaryOp<T>(T x, T y); static void Method<T>(BinaryOp<T> op, T p1, T p2) { Console.WriteLine(op(p1, p2)); } static void Main(){ Method((a, b) => a + b, 1, 2); } } Pavel JežekC# 3.0 and .NET 3.5
Lambda Expressions (2) • Expression or statement body • Implicitly or explicitly typed parameters • Examples: x => x + 1// Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, statement body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, statement body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters • A lambda expression is a value, that does not have a type but can be implicitly converted to a compatible delegate type delegate R Func<A,R>(A arg); Func<int,int> f1 = x => x + 1; // Ok Func<int,double> f2 = x => x + 1; // Ok Func<double,int> f3 = x => x + 1; // Error– double cannot be // implicitly converted to int Pavel JežekC# 3.0 and .NET 3.5
Lambda Expressions (3) • Lambda expressions participate in inference process of type arguments of generic methods • In initial phase, nothing is inferred from arguments that are lambda expressions • Following the initial phase, additional inferences are made from lambda expressions using an iterative process Pavel JežekC# 3.0 and .NET 3.5
Lambda Expressions (4) • Generic extension method example: public class List<T> : IEnumerable<T>, … { … } public static class Sequence { public static IEnumerable<S> Select<T,S>( this IEnumerable<T> source, Func<T, S> selector) { foreach (T element in source) yield return selector(element); } } • Calling extension method with lambda expression: List<Customer> customers = GetCustomerList(); IEnumerable<string> names = customers.Select(c => c.Name); • Rewriting extension method call: IEnumerable<string> names = Sequence.Select<T, S>(customers, c => c.Name); • T type argument is inferred to Customer based on source argument type Sequence.Select<Customer, S>(customers, c => c.Name) • c lambda expression argument type is infered to Customer Sequence.Select<Customer, S>(customers, (Customer c) => c.Name) • S type argument is inferred to string based on return value type of the lambda expression Sequence.Select<Customer, string>(customers, (Customer c) => c.Name) Pavel JežekC# 3.0 and .NET 3.5
Lambda Expressions: Expression Trees • Permit lambda expressions to be represented as data structures instead of executable code • Lambda expression convertible to delegate D (assignment causes code generation) is also convertible to expression tree (abstract syntax tree) of type System.Linq.Expressions.Expression<D> (assignment causes expression tree generation) • Expression trees are immutable value => (value + 2) * 1 Func<int, int> f = Expression<Func<int, int>> e = IL_0000: ldarg.0 IL_0001: ldc.i4.2 IL_0002: add IL_0003: ldc.i4.s 10 IL_0005: mul IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret LambdaExpression BinaryExpression BinaryExpression ParameterExpression(“value”) ConstantExpression(2) ConstantExpression(10)
Object Initializers • Class representing a point: public class Point{ private int x, y; public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } • New instance can be created using object initializer: var a = new Point { X = 0, Y = 1 }; • Which is equivalent to: var a = new Point(); a.X = 0; a.Y = 1; Pavel JežekC# 3.0 and .NET 3.5
Object Initializers (2) • Class representing a rectangle: public class Rectangle{ private Point p1, p2; public Point P1 { get { return p1; } set { p1 = value; } } public Point P2 { get { return p2; } set { p2 = value; } } } • New instance can be created using object initializer: var r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } }; • Which is equivalent to: var r = new Rectangle(); var __p1 = new Point(); __p1.X = 0; __p1.Y = 1; r.P1 = __p1; var __p2 = new Point(); __p2.X = 2; __p2.Y = 3; r.P2 = __p2; Pavel JežekC# 3.0 and .NET 3.5
Object Initializers (3) • Class representing a rectangle with constructor that allocates p1 and p2: public class Rectangle { private Point p1 = new Point(); private Point p2 = new Point(); public Point P1 { get { return p1; } } public Point P2 { get { return p2; } } } • New instance can be created using object initializer: var r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } }; • Which is equivalent to: var r = new Rectangle(); r.P1.X = 0; r.P1.Y = 1; r.P2.X = 2; r.P2.Y = 3; Pavel JežekC# 3.0 and .NET 3.5
Collection Initializers • Example: List<int> digits = new List<int> { 0, 1, 2}; • Is equivalent to: List<int> digits = new List<int>(); digits.Add(0); digits.Add(1); digits.Add(2); • List<T> has to implement System.Collections.Generic.ICollection<T> interface with the Add(T) method Pavel JežekC# 3.0 and .NET 3.5
Combining Object and Collection Initializers • Class representing a contact with name and list of phone numbers: public class Contact { private string name; private List<string> phoneNumbers = new List<string>(); public string Name { get { return name; } set { name = value; } } public List<string> PhoneNumbers { get { return phoneNumbers; } } } • List of contacts can be created and initialized with: var contacts = new List<Contact> { new Contact { Name = "Chris Smith", PhoneNumbers = { "206-555-0101", "425-882-8080" } }, new Contact { Name = "Bob Harris", PhoneNumbers = { "650-555-0199" } } }; • Which is equivalent to: var contacts = new List<Contact>(); var __c1 = new Contact(); __c1.Name = "Chris Smith"; __c1.PhoneNumbers.Add("206-555-0101"); __c1.PhoneNumbers.Add("425-882-8080"); contacts.Add(__c1); var __c2 = new Contact(); __c2.Name = "Bob Harris"; __c2.PhoneNumbers.Add("650-555-0199"); contacts.Add(__c2); Pavel JežekC# 3.0 and .NET 3.5
Auto-implemented Properties • Backed up by a private field normally inaccessible to programmer (only via the property): class LightweightCustomer { public double TotalPurchases { get; set; } public string Name { get; private set; } // read-only public int CustomerID { get; private set; } // read-only } Pavel JežekC# 3.0 and .NET 3.5
Anonymous Types • Following expression: new { p1 = e1 , p2 = e2 , …pn = en } • Can be used to define an anonymous type : class __Anonymous1{ private T1 f1 ; private T2 f2 ; … private Tn fn ; public T1 p1 { get { return f1 ; } set { f1 = value ; } } public T2 p2 { get { return f2 ; } set { f2 = value ; } } … public T1 p1 { get { return f1 ; } set { f1 = value ; } } } • And create its instance using object initializer • Different anonymous object initilizers that define properties with same names in the same order generate the same anonymous type: var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2; Pavel JežekC# 3.0 and .NET 3.5
Partial Methods • Can appear only in partial classes or structs, and must be void, private and without out parameters: partial class A { string _name; partial void OnNameChanged(); public string Name { set { _name = value; OnNameChanged(); } } } partial class A { partial void OnNameChanged() { // Do something } } Pavel JežekC# 3.0 and .NET 3.5
Query Expressions – Examples • Query expression: from c in customers where c.City == "London“ select c • Gets translated to: customers.Where(c => c.City == "London") Pavel JežekC# 3.0 and .NET 3.5
Query Expressions – Examples • Query expression: from c in customers where c.City == "London“ select c.Name • Gets translated to: customers.Where(c => c.City == "London").Select(c => c.Name) Pavel JežekC# 3.0 and .NET 3.5
Query Expressions – Examples • Query expression: from c in customers orderby c.Name select new { c.Name, c.Phone } • Gets translated to: customers.OrderBy(c => c.Name).Select(c => new { Name = c.Name, Phone = c.Phone }) Pavel JežekC# 3.0 and .NET 3.5
Query Expressions – Examples • Query expression: from c in customers where c.City == "London" from o in c.Orders where o.OrderDate.Year == 2005 select new { c.Name, o.OrderID, o.Total } • Gets translated to: customers.Where(c => c.City == "London"). SelectMany(c => c.Orders. Where(o => o.OrderDate.Year == 2005). Select(o => new { Name = c.Name, OrderID = o.OrderID, Total = o.Total }) ) Pavel JežekC# 3.0 and .NET 3.5
Query Expressions • Query expressions or LINQ (Language INtergrated Queries) are the key feature of .NET 3.5 • Query expressions are translated to method calls – works on classes like: delegate R Func<A,R>(A arg); class C<T> { public C<T> Where(Func<T,bool> predicate); public C<S> Select<S>(Func<T,S> selector); public C<S> SelectMany<S>(Func<T,C<S>> selector); public O<T> OrderBy<K>(Func<T,K> keyExpr); public O<T> OrderByDescending<K>(Func<T,K> keyExpr); public C<G<K,T>> GroupBy<K>(Func<T,K> keyExpr); public C<G<K,E>> GroupBy<K,E>(Func<T,K> keyExpr, Func<T,E> elemExpr); } … Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Creating Entity Classes • Only instances of “entity classes”, i.e. “entities”, can be stored in/retrieved from databases [Table] // Table is named “Customer” public class Customer { public string CustomerID; // Transient data not stored in DB public string City; // Transient data not stored in DB } [Table(Name="Customers")] public class Customer { public string CustomerID; // Transient data not stored in DB public string City; // Transient data not stored in DB } [Table(Name="Customers")] public class Customer { [Column(Id=true)] // Part of database primary key public string CustomerID; [Column] public string City; } Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL - DataContext • DataContext is equivalent of ADO.NET connection • It retrieves data (objects) from the database and submits changes • It translates requests (queries) for objects into SQL queries • Using the DataContext to retrieve customer objects whose city is London: // DataContext takes a connection string DataContextdb = new DataContext("c:\\northwind\\northwnd.mdf"); // Get a typed table to run queries Table<Customer> Customers = db.GetTable<Customer>(); // Query for customers from London var q = from c in Customers where c.City == "London" select c; foreach (varcust in q) {Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City); } Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Strongly Typed DataContext public partial class Northwind : DataContext { public Table<Customer> Customers; public Table<Order> Orders; public Northwind(string connection): base(connection) {} } • Retrieving customer objects whose city is London more easily: Northwind db = new Northwind("c:\\northwind\\northwnd.mdf"); var q = from c in db.Customers where c.City == "London“ select c; foreach (var cust in q) { Console.WriteLine("id = {0}, City = {1}",cust.CustomerID, cust.City); } Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Query Execution • Queries are not imperative – they are not executed immediately var q = from c in db.Customers where c.City == "London" select c; • Only creates an instance of IQueryable<Customer> type and assigns it to q • When the application tries to enumerate the contents of a query, it gets executed (i.e. deferred execution): // Execute first time foreach (Customer c in q) Console.WriteLine(c.CompanyName); // Execute second time foreach (Customer c in q) Console.WriteLine(c.CompanyName); Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Query Execution • Queries are not imperative – they are not executed immediately var q = from c in db.Customers where c.City == "London" select c; • Only creates an instance of IQueryable<Customer> type and assigns it to q • When the application tries to enumerate the contents of a query, it gets executed (i.e. deferred execution): // Execute db.Log = Console.Out; foreach (Customer c in q) Console.WriteLine(c.CompanyName); • Standard output: SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title],[t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension], [t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath] FROM [dbo].[Employees] AS [t0] WHERE [t0].[City] = @p0 -- @p0: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [London] Pavel JežekC# 3.0 and .NET 3.5
Expression Trees • Permit lambda expressions to be represented as data structures instead of executable code • Lambda expression convertible to delegate D (assignment causes code generation) is also convertible to expression tree (abstract syntax tree) of type System.Linq.Expressions.Expression<D> (assignment causes expression tree generation) • Expression trees are immutable value => (value + 2) * 1 Func<int, int> f = Expression<Func<int, int>> e = IL_0000: ldarg.0 IL_0001: ldc.i4.2 IL_0002: add IL_0003: ldc.i4.s 10 IL_0005: mul IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret LambdaExpression BinaryExpression BinaryExpression ParameterExpression(“value”) ConstantExpression(2) ConstantExpression(10) Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Advantage of Deferred Execution • Partial query contruction: var q = from c in db.Customers where c.City == "London" select c; if (orderByLocation) { q = from c in q orderby c.Country, c.City select c; } else if (orderByName) { q = from c in q orderby c.ContactName select c; } foreach (Customer c in q) Console.WriteLine(c.CompanyName); Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Avoiding Deferred Execution • Query can be converted to standard collection classes using ToList() or ToArray() Standard Query Operators – leads to immediate execution: var q = from c in db.Customers where c.City == "London" select c; // Execute once using ToList(), ToArray() var list = q.ToList(); foreach (Customer c in list) Console.WriteLine(c.CompanyName); foreach (Customer c in list) Console.WriteLine(c.CompanyName); Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Defining Relationships [Table(Name="Customers")] public class Customer { [Column(Id=true)] public string CustomerID; ... private EntitySet<Order> _Orders; [Association(Storage="_Orders", OtherKey="CustomerID")] public EntitySet<Order> Orders { get { return this._Orders; } set { this._Orders.Assign(value); } } } [Table(Name="Orders")] public class Order { [Column(Id=true)] public intOrderID; [Column] public string CustomerID; private EntityRef<Customer> _Customer; [Association(Storage="_Customer", ThisKey="CustomerID")] public Customer Customer { get { return this._Customer.Entity; } set { this._Customer.Entity = value; } } } Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Querying Across Relationships • Query using the Orders property to form the cross product between customers and orders, producing a new sequence of Customer and Order pairs: var q = from c in db.Customers from o in c.Orders where c.City == "London" select new { c, o }; • Another query producing the same result: var q = from o in db.Orders where o.Customer.City == "London" select new { c = o.Customer, o }; Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Modifying Entities Northwinddb = new Northwind("c:\\northwind\\northwnd.mdf"); // Query for a specific customer string id = "ALFKI"; varcust = db.Customers.Single(c => c.CustomerID == id); // Change the name of the contact cust.ContactName = "New Contact"; // Delete an existing Order Order ord0 = cust.Orders[0]; // Removing it from the table also removes it from the Customer’s list db.Orders.Remove(ord0); // Create and add a new Order to Orders collection Order ord = new Order { OrderDate = DateTime.Now }; cust.Orders.InsertOnSubmit(ord); // Ask the DataContext to save all the changes – generates appropriate SQL command db.SubmitChanges(); Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Creating Simple Databases [Table(Name="DVDTable")] public class DVD { [Column(Id = true)] public string Title; [Column] public string Rating; } public class MyDVDs : DataContext { public Table<DVD> DVDs; public MyDVDs(string connection) : base(connection) {} } // Creating a new database MyDVDs db = new MyDVDs("c:\\mydvds.mdf"); db.CreateDatabase(); // Replacing an existing one MyDVDs db = new MyDVDs("c:\\mydvds.mdf"); if (db.DatabaseExists()) { db.DeleteDatabase(); } db.CreateDatabase(); Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Inheritance [Table] [InheritanceMapping(Code = "C", Type = typeof(Car))] [InheritanceMapping(Code = "T", Type = typeof(Truck))] [InheritanceMapping(Code = "V", Type = typeof(Vehicle), IsDefault = true)] public class Vehicle { [Column(IsDiscriminator = true)] public string Key; [Column(Id = true)] public string VIN; [Column] public string MfgPlant; } public class Car : Vehicle { [Column] public int TrimCode; [Column] public string ModelName; } public class Truck : Vehicle { [Column] public int Tonnage; [Column] public int Axles; } Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Inheritance • Example queries: • var q = db.Vehicle.Where(p => p is Truck);var q = db.Vehicle.OfType<Truck>();var q = db.Vehicle.Select(p => p as Truck).Where(p => p != null);foreach (Truck p in q) Console.WriteLine(p.Axles); Pavel JežekC# 3.0 and .NET 3.5
LINQ to SQL – Query Visualizer in VS static void Main(string[] args) {Northwnd db = new Northwnd(@"C:\program files\linq preview\data\northwnd.mdf"); var q = from c in db.Customers where c.City == "London" select c; } // Breakpoint Pavel JežekC# 3.0 and .NET 3.5