TDDD49 Programmering C# och .NET Lecture 3 - 2015 Johannes Schmidt, Department of Computer and Information Science (IDA), Linköping University In this lecture: ● Anonymous types ● Collections ● LINQ Anonymous types Declare implicitly typed variables with var. The type will be determined by the compiler. var Record = new { ID = 225, Name = "CSJR", Description = "This is a record" }; var Employees no1 = new no2 = new no3 = new no4 = new }; = new {Name {Name {Name {Name { = = = = "Harald", Age = 34}, "Charlie", Age = 31}, "Alice", Age = 54}, "Bob", Age = 20} Console.WriteLine(Employees.no1.Name); Anonymous type != undefined type A variable declared as var has a very well-defined type. The type is determined by the compiler, but is otherwise fixed and well-specified. var Record1 = new { Name = "Program", Age = 42 }; var Record2 = new { Name = "Donner", Age = 7 }; var Record3 = new { FirstName = "Tubo", Age = 77 }; var Record4 = new { Age = 101, Name = "Voix" }; Record2 = Record1; Record2 = Record3; Record2 = Record4; // ok, same type // Err: Cannot convert AnonymousType#2 to AnonymousType#1 // Err: Cannot convert AnonymousType#3 to AnonymousType#1 Anonymous types as parameter types? A variable declared as var can only be passed as a parameter to a method if the method's parameter type is object or if we use method type inference: public static void method(var parameter) { Console.WriteLine(parameter); } // does not work/compile! public static void methodA(object parameter) { // works Console.WriteLine(parameter); } public static void methodB<T>(T parameter) { Console.WriteLine(parameter); } var Record1 = new { methodA(Record1); methodB(Record1); // works, preferred Name = "Program", Age = 42 }; // works // works, preferred since type safer When to use Anonymous types Rule of Thumb: avoid anonymous types. Use them only when the type is clear from the context, and where it increases code readability. // good example: // write var items = Dictionary<string, List<Account>>(); // instead of Dictionary<string, List<Account>> items = Dictionary<string, List<Account>>(); // bad example: var result = getData(); Anonymous types are particularly important when it comes to aggregation of data from multiple types (i.e. LINQ, see later on). What is a Collection? Anything that implements System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T>. These interfaces allow to iterate over the collection (e.g. with foreach). Also ordinary arrays count as Collection. That is: Arrays + all classes from System.Collections and System.Collections.Generic: Ordinary array + ArrayList BitArray Dictionary SortedDictionary SortedList LinkedList List Queue Stack SortedSet … foreach on ordinary arrays int[] array = new int[] { 1, 2, 3, 4, 5, 6 }; foreach (int item in array) { Console.WriteLine(item); } // translates by the compiler to for(int counter = 0; counter < array.Length; counter++) { int item = array[counter]; Console.WriteLine(item); } foreach on real Collections Every collection implements the interface System.Collections.Generic.IEnumerator<T> or System.Collections.IEnumerator This provides the method GetEnumerator() which delivers an IEnumerator and with it the method MoveNext() and the property Current. var stack = new System.Collections.Generic.Stack<int>(); foreach (int item in stack) { Console.WriteLine(item); } // translates by the compiler to System.Collections.Generic.Stack<int>.Enumerator enumerator; enumerator = stack.GetEnumerator(); while(enumerator.MoveNext()) { int item = enumerator.Current; } Console.WriteLine(item); Collection initialisers List<string> MyDictionary; // write MyDictionary = new List<string>() { "Ice", "Equipement", "Sea", "Goofy" }; // instead of MyDictionary.Add("Ice"); MyDictionary.Add("Equipement"); MyDictionary.Add("Sea"); MyDictionary.Add("Goofy"); // (this is what the comipler does then for you) Available whenever the .Add() method is available (this excludes e.g. Stacks and Queues). LINQ LINQ = Language Integrated Query Language In C# any collection can be queried with standard query operators (in database terms, a collection is just a database). An SQL-like syntax is available. A LINQ query consists of three parts: // 1. Define/get data source. int[] numbers = new int[7] { 41, 24, 16, 7, 10, 2, 17, 38, 58 }; // 2. create query IEnumerable<int> numQuery = from num in numbers where (num < 38) select num; // 3. excecute query foreach (int num in numQuery) { Console.Write(num + " "); } Data source public class Person { public string Name; public int Age; public long DepartmentID; public void Display() { Console.WriteLine("Name: " + Name + "\tAge: " + Age + "\tDep: " + DepartmentID); } } var var var var var Person1 Person2 Person3 Person4 Person5 = = = = = new new new new new Person() Person() Person() Person() Person() { { { { { Name Name Name Name Name = = = = = "Harald", "Hanne", "Harald", "Alice", "Bob", Age Age Age Age Age = = = = = 45, 32, 22, 28, 37, DepartmentID DepartmentID DepartmentID DepartmentID DepartmentID = = = = = 0 1 2 1 4 }; }; }; }; }; var MyList = new List<Person>() {Person1, Person2, Person3, Person4, Person5}; Filtering with Where() IEnumerable<Person> WhereData = MyList.Where(x => x.Age < 29); foreach (Person x in WhereData) { x.Display(); } Output: Name: Harald Name: Alice Age: 22 Age: 28 Dep: 2 Dep: 1 // You can also define and excecute the query in one step: foreach (var x in MyList.Where(x => x.Age < 29)) { x.Display(); } Projecting with Select() // select parts and change type with Select IEnumerable<double> SelectData = MyList.Select(x => (double)x.Age * 1.5); foreach (double x in SelectData) { Console.Write(x + " "); } Output: 67.5 48 33 42 55.5 Projecting with Select() var SelectData = MyList.Select(x => new { Name = x.Name, Sex = getSex(x) }); foreach (var item in SelectData) { Console.Writeln(item); } Output: {Name = {Name = {Name = {Name = {Name = Harald, Sex = m} Hanne, Sex = f} Harald, Sex = m} Alice, Sex = f} Bob, Sex = m} Count with Count() // count all elements Console.WriteLine( MyList.Count() ); // count only those between 25 and 40 Console.WriteLine( MyList.Where(x => x.Age > 25 && x.Age < 40).Count() ); // the same, but more elegant Console.WriteLine( MyList.Count(x => x.Age > 25 && x.Age < 40) ); Output: 5 3 3 Deferred execution A query is only executed when necessary. That is, usually when iterating over it with a foreach statement. IEnumerable<int> data = MyList.Select(x => { Console.Write("Et voila "); return x.Age; }); Console.WriteLine("Foreach 1"); foreach (int x in data) { Console.WriteLine(x); } Output: Foreach 1 Et voila 45 Et voila 32 Et voila 22 Et voila 28 Et voila 37 Deferred execution A query is only executed when necessary. That is, usually when iterating over it with a foreach statement. IEnumerable<int> data = MyList.Select(x => { Console.Write("Et voila "); return x.Age; }); Console.WriteLine("Foreach 1"); foreach (int x in data) { Console.WriteLine(x); } Console.WriteLine("Foreach 2"); foreach (int x in data) { Console.WriteLine(x); } Console.WriteLine("Foreach 3"); foreach (int x in data) { Console.WriteLine(x); } Output: Foreach 1 Et voila 45 Et voila 32 Et voila 22 Et voila 28 Et voila 37 Foreach 2 Et voila 45 Et voila 32 Et voila 22 Et voila 28 Et voila 37 Foreach 3 Et voila 45 Et voila 32 Et voila 22 Et voila 28 Et voila 37 Enforce execution Execution can also be enforced with ToArray, ToList, ToDictionary IEnumerable<int> data = MyList.Select(x => { Console.Write("Et voila "); return x.Age; }).ToList(); Console.WriteLine("Foreach 1"); foreach (int x in data) { Console.WriteLine(x); } Console.WriteLine("Foreach 2"); foreach (int x in data) { Console.WriteLine(x); } Console.WriteLine("Foreach 3"); foreach (int x in data) { Console.WriteLine(x); } Output: Et voila Et voila Et voila Et voila Et voil Foreach 1 45 32 22 28 37 Foreach 2 45 32 22 28 37 Foreach 3 45 32 22 28 37 Deferred execution b) A query is only executed when necessary. That is, usually when iterating over it with a foreach statement. IEnumerable<int> Data = MyList.Select(x => x.Age); Console.WriteLine("Executing:"); foreach (int x in Data) { Console.Write(x + " "); } Console.WriteLine(); // modify data MyList.Remove(Person3); MyList.Remove(Person4); // myList has changed... Console.WriteLine("Executing again:"); foreach (int x in Data) { Console.Write(x + " "); } Output: Executing: 45 32 22 28 37 Executing again: 45 32 37 Enforce execution b) Execution can also be enforced with ToArray, ToList, ToDictionary IEnumerable<int> Data = MyList.Select(x => x.Age); Console.WriteLine("Executing:"); foreach (int x in Data) { Console.Write(x + " "); } Console.WriteLine(); // enforce execution and take a copy IEnumerable<int> Data2 = Data.ToList(); // modify data MyList.Remove(Person3); MyList.Remove(Person4); Output: Executing: 45 32 22 28 37 Executing again: 45 32 22 28 37 // myList has changed... // but we took a copy of the already executed query Console.WriteLine("Executing again:"); foreach (int x in Data2) { Console.Write(x + " "); } Order with OrderBy() Also hierarchical ordering is possible, with ThenBy() // Order with OrderBy() IEnumerable<Person> OrderedData = MyList.OrderBy(x => x.Name); foreach (Person x in OrderedData) { x.Display(); } // Order with OrderBy() and then ThenBy() IEnumerable<Person> OrderedDataB = MyList.OrderBy(x => x.Name) .ThenBy(x => x.Age); foreach (Person x in OrderedDataB) { x.Display(); } // Order with OrderByDescending() IEnumerable<Person> OrderedDataC = MyList.OrderByDescending(x => x.Name); foreach (Person x in OrderedDataC) { x.Display(); } Output: Alice Bob Hanne Harald Harald 28 37 32 45 22 Alice Bob Hanne Harald Harald 28 37 32 22 45 Harald Harald Hanne Bob Alice 22 45 32 37 28 Additional data public class Department { public long ID; public string Name; public void Display() { Console.WriteLine("ID: " + ID + "\tName: " + Name); } } var var var var var Dep1 Dep2 Dep3 Dep4 Dep5 = = = = = new new new new new Department() Department() Department() Department() Department() { { { { { ID ID ID ID ID = = = = = 0, 1, 2, 3, 4, Name Name Name Name Name = = = = = "IDA" "IFM" "IKK" "MAI" "ISY" }; }; }; }; }; var DepList = new List<Department>() { Dep1, Dep2, Dep3, Dep4, Dep5}; All data, summary var var var var var Person1 Person2 Person3 Person4 Person5 = = = = = new new new new new Person() Person() Person() Person() Person() { { { { { Name Name Name Name Name = = = = = "Harald", "Hanne", "Harald", "Alice", "Bob", Age Age Age Age Age = = = = = 45, 32, 22, 28, 37, DepartmentID DepartmentID DepartmentID DepartmentID DepartmentID = = = = = 0 1 2 1 4 }; }; }; }; }; var MyList = new List<Person>() {Person1, Person2, Person3, Person4, Person5}; var var var var var Dep1 Dep2 Dep3 Dep4 Dep5 = = = = = new new new new new Department() Department() Department() Department() Department() { { { { { ID ID ID ID ID = = = = = 0, 1, 2, 3, 4, Name Name Name Name Name = = = = = "IDA" "IFM" "IKK" "MAI" "ISY" }; }; }; }; }; var DepList = new List<Department>() { Dep1, Dep2, Dep3, Dep4, Dep5}; Join with Join() var items = MyList.Join( DepList, person => person.DepartmentID, dep => dep.ID, (person, dep) => new { // // // // Collection that shall be joined with select key1 (to match key2) select key2 create the new joined data Name = person.Name, Age = person.Age, DepartmentID = person.DepartmentID, DepartmentID2 = dep.ID, // ID2 just for illustration purpose DepartmentName = dep.Name}); foreach (var x in items){ Console.WriteLine("Name: " + x.Name + "\tAge: " + x.Age + "\tDep: " + x.DepartmentID + "\tDep2: " + x.DepartmentID2 + "\tDepName: " + x.DepartmentName); } Output: Name: Name: Name: Name: Name: Harald Hanne Harald Alice Bob Age: Age: Age: Age: Age: 45 32 22 28 37 Dep: Dep: Dep: Dep: Dep: 0 1 2 1 4 Dep2: Dep2: Dep2: Dep2: Dep2: 0 1 2 1 4 DepName: DepName: DepName: DepName: DepName: IDA IFM IKK IFM ISY And many more GroupBy OfType Union Concat Intersect Distinct Except SequenceEquals Reverse SelectMany GroupJoin ... - group by some criteria get only items of a certain type union without duplicates union (duplicates possible) intersection get rid of duplicates set minus check for equivalence, including the order reverse the order resolve collections of collections similar to Join On numeric values: Max, Min, Sum, Average LINQ query expressions You can use an alternative syntax with keywords. With query expressions the syntax can be SQL-like. Start with from … in … and close with select … You can combine both syntaxes. int[] numbers = new int[7] { 41, 24, 16, 7, 10, 2, 17, 38, 58 }; // instead of var numQuery = numbers.Where(num => num < 38); // write var numQuery = from num in numbers where (num < 38) select num; LINQ query expressions // instead of var newData = MyList.Where(person => person.Age < 38).Select(person => new { person.Name, person.Age }); // write var newData = from person in MyList where (person.Age < 38) select new {person.Name, person.Age}; foreach (var x in newData) { Console.WriteLine(x); } Output: {Name = {Name = {Name = {Name = Hanne, Age = 32} Harald, Age = 22} Alice, Age = 28} Bob, Age = 37} LINQ query expressions let – create a local variable in a query expression var newData = from person in MyList where (person.Age < 38) select new { person.Name, person.Age }; // equivalent var newData = from person in MyList let Age = person.Age where (Age < 38) select new { person.Name, Age }; into – continue a query in a query expression (use the output of a query as the input of another query) flattening – use multiple from clauses to achieve SelectMany See LINQ on msdn for further details and examples. What to do with LINQ Transform and filter data in a uniform way, independently of its source or form. ● ● ● ● LINQ to Objects (IEnumerable Collections) LINQ to SQL (access SQL databases via LINQ) LINQ to XML (query and modify XML documents via LINQ) LINQ to DataSet (cached data from different sources, see ADO.NET) Links and Literature Essential C# 5.0 by Mark Michaelis https://msdn.microsoft.com/en-us/library/bb397926.aspx LINQ http://msdn.microsoft.com/en-us/library/bb397919.aspx LINQ to Objects http://msdn.microsoft.com/en-us/library/bb386976.aspx LINQ to SQL http://msdn.microsoft.com/en-us/library/bb387098.aspx LINQ to XML http://msdn.microsoft.com/en-us/library/bb386977.aspx LINQ to DataSet https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b 101 LINQ samples