What's new in C# 3.0? Lambda Functions, Extension Methods, Automatic Properties, Type Inference, Object Initialization, Anonymous Types, Collection Initializers, Expression Trees, Partial Functions, and LINQ - Language Integrated Queries
LINQ, Language Integrated Query, is a great new feature of C# 3.0. This page gives a small overview of LINQ. Before we start with LINQ we have to understand some new features of C# 3.0 that were added so LINQ could work.
- Lambda Functions
Lambdas are anonymous delegates and can be used anywhere a delegate may appear. Behind the scenes the compiler will take the lambda function and create a class and a method with the contents of the lambda function. It then replaces the lambda function with a delegate pointing to the newly created class and function.
Lambda functions are a list of inputs, the "=>" symbol, and a body.
Lambdas come in two flavors: Lambda Expressions which are a simple phase of C# and Lambda Statements which have multiple lines of code surrounded by braces.
x => x * 0.05 //expression lambda (x, y) => { // lambda statements uses curly braces foo(x,y); bar(y); } () => { //lambda with no arguments just has empty parens foo(); bar(); }
We can make using delegates shorter by using a lambda expression:
// CalculateTax is a delegate that takes a double and returns a double public delegate double CalculateTax(double x); static void Main(string[] args) { CalculateTax stateDelegate = x => x * 0.05; CalculateTax federalDelegate = x => { if (x > 1000.0) return x * 0.02; else return 0.0; }; double amountOfPurchase = 12.99; Console.WriteLine("{0}", stateDelegate(amountOfPurchase)); Console.WriteLine("{0}", federalDelegate(amountOfPurchase)); Console.In.ReadLine(); }
When using more than one input variable, surround the input with parenthesis and separate with commas.
using System; namespace PlayingAround { class Lambda { public delegate int Largest(int x, int y); public static Largest myLargest = (int x, int y) => { return x > y ? x : y; }; private static void Main() { Console.Out.WriteLine("myLargest(4,5) = " + myLargest(4,5)); //prints 5 Console.Out.Write("press return to exit."); Console.In.ReadLine(); } } }
Although these examples are painfully contrived, the common use of lambdas are in LINQ being used as anonymous delegates.
Passing a lambda function to a method with void return type
If we want to pass a lambda function that has a void return type and a single argument we use the system "Action" delegate. What we'd really like to do is use something like "Func<string,void>", but "void" is not a valid type, so instead we must use the "Action" delegate.
using System; namespace PlayingAround { class MyAction { public static void Main() { Action<string> printMe = x => Console.Out.Write(x); Action<string> printMeAllCaps = x => Console.Out.Write(x.ToUpper()); ExecuteThreeTimes(printMe, "."); ExecuteThreeTimes(printMeAllCaps, "aBc"); //produces "...ABCABCABC" Console.Out.Write("press return to exit.");Console.In.ReadLine(); } private static void ExecuteThreeTimes(Action<string> func, string s) { func(s); func(s); func(s); } } }
- Using Func
To prevent redefining delegates over and over, Anders, the designer of C#, could have created something like this:
public delegate void TakesAnIntAndReturnsVoid(int i); public delegate int TakesAnIntAndReturnsInt(int i); public delegate void TakesADoubleAndReturnsVoid(double x); public delegate double TakesADoubleAndReturnsADouble(double x); ...
Then with a few thousand of these defined, you wouldn't need to create a delegate, just use one from the framework.
But fortunately though the magic of generics, we can specify the types in a generic delegate.
Functions are now a first class citizens in .Net. You can declare them with "Func". Func's take all their argument types and then their return type at the end as shown below:
Behind the scenes, Anders and company have defined default delegates for us:
public delegate void Action(); public delegate void Action<T1, T2>(T1 arg1, T2 arg2); public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3); public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); ... public delegate TResult Func<TResult>(); public delegate TResult Func<T, TResult>(T arg); public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2); ...
using System; namespace PlayingAround { class MyFunc { public static void Main() { //this function takes a string and returns an int Func<string, int> countMe = x => { return x.Length; }; LogMeInt(countMe, "lamb"); Console.Out.Write("press return to exit.");Console.In.ReadLine(); } private static int LogMeInt(Func<string, int> func, string s) { int count = func(s); Console.Out.WriteLine("count = " + count);//in real life we'd log the results to a db or something return count; } } }
- Extension Methods
In C#3.0 one of the very cool concepts is "Open Classes" where you can add methods to existing classes just like in Ruby and JavaScript. In C# parlance this is an extension method. Below is an example of adding a new method to our old friend "string".
using System; namespace PlayingAround { public static class MyStringExtensionClass { //wraps an xml tag around a string public static string Wrap(this string s, string tag) { return "<" + tag + ">" + s + "</" + tag + ">"; } } class TestMe { static void Main(string[] args) { Console.WriteLine("Warning".Wrap("h1")); // <h1>Warning</h1> Console.In.ReadLine(); } } }
- Automatic Properties
In the old days before c# 3.0 you had to manually create the hidden variable behind fields:
using System; using System.Collections.Generic; public class Book { private string _author; private string _title; public string Author { get{return _author;} set{_author = value;} } public string Title { get{return _title;} set{_title = value;} } public static void Main() { Book myBook = new Book(); myBook.Title = "Gates of Fire"; myBook.Author = "Stephen Pressfield"; Console.In.ReadLine(); } }
With Automatic Properties, the compiler will generate a private field for you. You can change this later if you need to do something more elegant than just get the private field's value.
using System; using System.Collections.Generic; public class Book { public string Author { get; set; } public string Title { get; set; } public static void Main() { Book myBook = new Book(); myBook.Title = "Gates of Fire"; myBook.Author = "Stephen Pressfield"; Console.WriteLine(myBook.Title); Console.In.ReadLine(); } }
- Local Variable Type Inference
The compiler can infer the type of a variable from the expression on the right side. These are useful in LINQ expressions when the types can be overwhelming to enter, trust me. The type can be something like a function that takes a function with two ints and a float, and another function that takes a string and returns a function that takes two ints and a function that...
It is important to remember that this is not a type-less variable, the compiler just infers and checks it at compile time.
var i = 6; Console.Out.WriteLine("i=" + i);
- Object Initialization
Objects can now be initialized in a more concise manner as shown below:
... public class House { public double price; public int stories; public string exterior; } class TestMe { static void Main(string[] args) { //old style House myOldHouse = new House(); myOldHouse.price = 200000.00; myOldHouse.stories = 1; myOldHouse.exterior = "wood"; //new style House myNewHouse = new House { price = 250000.00, stories = 2, exterior = "brick" }; Console.In.ReadLine(); } }
- Anonymous Types
We can create anonymous types. The compiler will give our creation a hidden name.
var animal = new { IsMammal = true , Name = "lion" }; Console.Out.WriteLine("Name=" + animal.Name); Console.Out.WriteLine("is mammal? " + animal.IsMammal);
- Collection Initializers
Instead of the klunky:
ArrayList ab = new ArrayList(); ab.Add("a"); ab.Add("b");
Using the collection initializer we can write
ArrayList ab = new ArrayList {"a", "b"};
- Expression Trees
This is a new feature in C# 3.0, which is not used by a typical developer, but are used by creators of LINQ-enabled storage devices.
- Partial Functions
Partial functions allow class designers to put in hooks for future use. If the functions are never given substance, the compiler ignores them.
using System; namespace PlayingAround { public partial class MyTest { partial void myPreHook();//never given substance, ignored by compiler public MyTest() { myPreHook(); Console.Out.WriteLine("Creating MyTest."); } public static void Main() { MyTest myTest = new MyTest(); Console.Out.Write("press return to exit."); Console.In.ReadLine(); } } }
Adding a definition for myPreHook() will cause the compiler to generate the code and it will be executed.
public partial class MyTest { partial void myPreHook() { Console.Out.WriteLine("myPreHook."); } }
- LINQ - Language Integrated Query
LINQ is designed to work with any storage medium, e.g., SQL, XML, DataSets, or objects.
LINQ has two syntaxes: query expression which is kinda SQL-like or standard dot notation which is standard c# . I prefer the dot notation since it really shows what is happening and it works with all query operators.
Make sure you have a reference to "System.data.Linq.dll" in your solution.
- Types of LINQ queries
- Sequence to Sequence - IEnumerable<TSource> to IEnumerable<TSource>
- Filtering - Where, Take, TakeWhile, Skip, SkipWhile, Distinct
- Projecting - Select, SelectMany
- Joining - Join, GroupJoin, Zip
- Ordering - OrderBy, ThenBy, Reverse
- Grouping - GroupBy
- Set Operators - Concat, Union, Intersect, Except
- Conversion methods - OfType, Cast, ToArray, ToList, ToDictionary, ToLookup, AsEnumerable, AsQueryable
- Sequence to Element or value - IEnumerable<TSource> to <TSource>
- Search operators - First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty
- Aggregation methods - Aggregate, Average, Count, LongCount, Sum, Max, Min
- Quantifiers or Logical - All, Any, Contains, SequenceEqual
- void to Sequence - Void to IEnumerable<TSource>
- Generation methods - Empty, Range, Repeat
- Sequence to Sequence - IEnumerable<TSource> to IEnumerable<TSource>
- Examples of Filtering -
Where, Take, TakeWhile, Skip, SkipWhile, Distinct
using System; using System.Collections.Generic; using System.Linq; namespace Practice { class LinqFiltering {//demo of Where, Take, TakeWhile, Skip, SkipWhile, Distinct static void Main() { //create ienumerable sequence from 0 to 20 IEnumerable<int> zeroToTwenty = Enumerable.Range(0,21); //select only number that are multiples of 5 IEnumerable<int> multiplesOfFive = zeroToTwenty.Where(n => n%5 == 0);//0,5,10,15,20 //Where takes an option second argument which is the index into the array //lets take mulitples of 5 that are odd IEnumerable<int> OddMultiplesOfFive = zeroToTwenty.Where((n,i) => (n % 5 == 0) && i%2>0);//5,15 //take only the first three IEnumerable<int> firstThree = zeroToTwenty.Take(3);//012 //take elements until the expression is false IEnumerable<int> lessThanFive = zeroToTwenty.TakeWhile(n => n < 5);//0,1,2,3,4 //skip elements until the expression is true, then lets take the rest IEnumerable<int> greaterThanTen = zeroToTwenty.SkipWhile(n => n < 10);//10,11,12,13,14,15,16,17,18,19,20 //get only the unique elements var enumerable = new List<int> { 1,2,3,3,4,4}.Distinct();//1,2,3,4 Console.Write("Press 'Enter' to exit.");Console.In.ReadLine(); } } }
- Examples of Projection - Select, SelectMany
"Select" just projects the objects defined in the lambda function.
"SelectMany" flattens out a "list of lists" into a single list. (Really its an "IEnumerable of IEnumerables", but that doesn't quite roll of the tongue as easily as "list of lists".
using System; using System.Collections.Generic; using System.Linq; namespace Practice { class Student { public IEnumerable<string> Pets {get; set; } public string Name; } class Selects { static void Main() { List<Student> students = new List<Student> { new Student { Name = "Jackie", Pets = new List<string> {"dog", "fish", "gerbil"} }, new Student { Name = "Mikie", Pets = new List<string> {"rabbit", "fish", "gorilla"} } }; IEnumerable<string> names = students.Select(s => s.Name); names.ToList().ForEach(Console.WriteLine); //names = Jackie, Mikie IEnumerable <IEnumerable<string>> pets = students.Select(s => s.Pets); //pets = System.Collections.Generic.List`1[System.String] // System.Collections.Generic.List`1[System.String] pets.ToList().ForEach(Console.WriteLine); //flatten the lists into one list IEnumerable<string> allPets = students.SelectMany(s => s.Pets); allPets.ToList().ForEach(Console.Write); //allPets = dog,fish,gerbil,rabbit,fish,gorilla Console.Write("Press 'Enter' to exit.");Console.In.ReadLine(); } } }
- Join - Join, GroupJoin, and ZIP
"Join" and "GroupJoin" are similar to "Select" and "SelectMany", but may be more efficient at times, but less flexible since they only do inner and left outer joins.
"Join" matches elements from two collections and returns a flat set. The SQL equivalent is "INNER JOIN".
Syntax: OuterCollection.Join(innerCollection,OuterKey,InnerKey,resultSelector);
"GroupJoin" is like "Join", but returns a hierarchical result set.
"Zip" works like a zipper and operates on pairs of values from two different collections.
using System; using System.Collections.Generic; using System.Linq; namespace Practice { class Zipper { static void Main() { List<string> japanese = new List<string> {"ichi", "ni", "san", "shi", "go"}; List<string> english = new List<string> {"one", "two", "three", "four", "five"}; IEnumerable<string> translation = english.Zip(japanese, (e, j) => e + " in japanese is " + j); translation.ToList().ForEach(Console.WriteLine); /* result: one in japanese is ichi two in japanese is ni three in japanese is san four in japanese is shi five in japanese is go */ Console.Write("Press 'Enter' to exit."); Console.In.ReadLine(); } } }
- Sorting
OrderBy(),ThenBy()
using System; using System.Collections; using System.Collections.Generic; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class Order { public class Employee { public string last; public string first; public double salary; public Employee(string last, string first, double salary) { this.last = last; this.first = first; this.salary = salary; } override public string ToString() {return string.Format("{0}, {1}: ${2}", last, first, salary);}} public static void Main() { Employee[] employees = { new Employee("Jones", "Sara", 11000.00), new Employee("Jones", "Brad", 10000.00), new Employee("Aaron", "Mike", 10000.00), new Employee("Xader", "Xena", 20000.00) }; Print("No ordering", employees); Print("Order by last", employees.OrderBy(n => n.last)); Print("Order by last, first",employees.OrderBy(n => n.last).ThenBy(n => n.first)); Print("order by salary", employees.OrderBy(n => n.salary)); Print("order by salary descending", employees.OrderByDescending(n => n.salary)); Console.Out.Write("press return to exit.");Console.In.ReadLine(); } static void Print(string title, IEnumerable<Employee> objects) { Console.WriteLine(title+":"); objects.ToList().ForEach(n => Console.WriteLine(n)); } } }
Produces:
No ordering: Jones, Sara: $11000 Jones, Brad: $10000 Aaron, Mike: $10000 Xader, Xena: $20000 Order by last: Aaron, Mike: $10000 Jones, Sara: $11000 Jones, Brad: $10000 Xader, Xena: $20000 Order by last, first: Aaron, Mike: $10000 Jones, Brad: $10000 Jones, Sara: $11000 Xader, Xena: $20000 order by salary: Jones, Brad: $10000 Aaron, Mike: $10000 Jones, Sara: $11000 Xader, Xena: $20000 order by salary descending: Xader, Xena: $20000 Jones, Sara: $11000 Jones, Brad: $10000 Aaron, Mike: $10000 press return to exit.
- Set Operators
Union, Intersect, Concat, Distinct, and Except.
"Union" joins the lists, but only keeps one copy of each element.
"Intersect" returns only elements in common.
"Except" returns all elements of the first sequence that do not exist in the second.
"Concat" joins the lists and keeps all elements, even duplicates.
"Distinct" returns only the unique items in a list.
using System; using System.Collections.Generic; using System.Linq; namespace PlayingAround { class Sets { private static void Main() { int[] little = {0, 1, 2, 3, 4, 5, 6}; int[] big = {5, 6, 7, 8, 9, 10}; IEnumerable<int> all = little.Union(big); all.ToList().ForEach(n => Console.Write(n)); //writes 012345678910 Console.WriteLine(); IEnumerable<int> intersect = little.Intersect(big); intersect.ToList().ForEach(n => Console.Write(n)); //writes 56 Console.WriteLine(); IEnumerable<int> both = little.Concat(big); both.ToList().ForEach(n => Console.Write(n)); //writes 01234565678910 Console.WriteLine(); IEnumerable<int> dist = both.Distinct(); dist.ToList().ForEach(n => Console.Write(n)); //writes 012345678910 Console.WriteLine(); //"Except()" returns all elements of the first sequence that do not exist in the second IEnumerable<int> exceptional = little.Except(big); exceptional.ToList().ForEach(n => Console.Write(n)); //writes 01234 Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
- GroupBy
GroupBy does the SQL equivalent of, wait for it ..., "Group By". It takes an IEnumerable<T> and creates IEnumerable<IGrouping<string,string>>.
- Conversions
OfType and Cast take an old fashioned non-generic Enumerable and convert it to a IEnumerable<T>.
"OfType" is like "Cast" except "Cast" throws an exception when the cast cannot be made, while "OfType" ignores incompatabilities.
ArrayList nonGeneric = new ArrayList(); nonGeneric.AddRange(new string[] {"red","green","blue"}); IEnumerable<string> generic = nonGeneric.Cast<string>(); generic.ToList().ForEach(Console.WriteLine);
- Searching for items
First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty
using System; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class NonDeferred2 { private static void Main() { string[] names = {"Cindy", "Bobby", "Jan", "Peter", "Marcia", "Greg"}; // -- Finding an item //First grabs the first item Console.WriteLine(names.First()); //writes: "Cindy" Console.WriteLine(names.First(p => p.StartsWith("J"))); //writes: "Jan" Console.WriteLine(names.Last()); //writes: "Greg" //selects the last item that matches a function Console.WriteLine(names.Last(p => p.Length>4)); //writes: "Marcia" Console.WriteLine(names.LastOrDefault(p => p.Length > 8)); //writes: // -- Seaching for items //Single returns the only element of a sequence, or throws an exception try { Console.WriteLine(names.Single()); //throws InvalidOperationException: } catch(InvalidOperationException) { Console.WriteLine("I was expecting only one element"); } //with a predicate it returns the only one, or throws an exception Console.WriteLine(names.Single(p=>p.StartsWith("B"))); //writes: "Bobby" //ElementAt selects the element at that index (zero based) Console.WriteLine(names.ElementAt(2)); //writes: "Jan" Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
- Common math functions
Count, Sum, Min, Max, and Average
using System; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class Maths { private static void Main() { int[] fibs = {0, 1, 1, 2, 3, 5, 8}; Console.WriteLine(fibs.Count()); //writes: 7 Console.WriteLine(fibs.Count(p => p < 3)); //writes: 4 Console.WriteLine(fibs.Sum()); //writes: 20 Console.WriteLine(fibs.Min()); //writes: 0 Console.WriteLine(fibs.Max()); //writes: 8 Console.WriteLine(fibs.Average()); //writes: 2.85714285714286 Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
"Any" returns true if any of the predicate values is true. "All" only returns true if all predicate values return true. "Contains" returns true if the object is found in the collection.. "SequenceEqual" returns true if two collections are equal.
using System; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class NonDeferred2 { private static void Main() { string[] names = {"Cindy", "Bobby", "Jan", "Peter", "Marcia", "Greg"}; bool eq = names.SequenceEqual(names.Reverse().Reverse()); //returns true //Any() returns true if the sequence has any elements Console.WriteLine(names.Any()); //writes: "true" //Any(p) return true if any element matches the predicate Console.WriteLine(names.Any(p => p.Length>3)); //writes: "true" //All(p) returns true only if all the elements match the condition Console.WriteLine(names.All(p => p.Length > 4)); //writes: "false" //Contains returns true if that object is in the sequence Console.WriteLine(names.Contains("Carol")); //writes: "false" Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
- Generation functions
Range, Reverse, Repeat, and Empty
using System; using System.Collections.Generic; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class Range { private static void Main() { //note: Range. Empty and Repeat are not extension methods, but static ones IEnumerable<int> one2five = Enumerable.Range(1, 5); one2five.ToList().ForEach(n => Console.Write(n)); //writes: 12345 IEnumerable<int> five2one = one2five.Reverse(); five2one.ToList().ForEach(n => Console.Write(n)); //writes: 54321 IEnumerable<int> fivethrees = Enumerable.Repeat(3,5); fivethrees.ToList().ForEach(n => Console.Write(n)); //writes: 33333 //Empty creates an empty Enumerable of the specific type IEnumerable<int> empty = Enumerable.Empty<int>(); fivethrees.ToList().ForEach(n => Console.Write(n)); //writes: Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
- Types of LINQ queries
- Mixed Examples and Miscellanous Code
Below is an example using some of the LINQ features for in-memory objects. LINQ enables VStudio to give intelliSense to some queries. For in-memory LINQ, it's all about using the Standard Query Operators. These are extension methods on the IEnumerable<T> class, methods like "Where()","Select()","Sum()" and "Average()".
- Example using Standard Query Operators
This shows the use of the Where(), OrderBy(), Count(), Sum(), Min(), Select(), and ForEach() methods.
using System; using System.Collections.Generic; using System.Linq; namespace PlayingAround { internal class Photo { public Photo(string photographer, string subject, int year, double cost) { this.photographer = photographer; this.subject = subject; this.year = year; this.cost = cost; } public string photographer { get; set; } public string subject { get; set; } public int year { get; set; } public double cost { get; set; } public override string ToString() { return photographer + ", " + subject + ", " + year + ", " + cost; } } internal class PhotoLab { private static void Main(string[] args) { List<Photo> list = new List<Photo> { new Photo("Frank", "Cats", 2002, 1.10), new Photo("Lee", "Dogs and Cats", 2003, 2.05), new Photo("Sarah", "Stingrays", 2007, 23.00), new Photo("Jon", "Meerkats", 2005, 16.75) }; //simple where clause var oldPhotos = list.Where(p => p.year == 2003); Console.Out.WriteLine("oldPhotos.Count() = {0}", oldPhotos.Count()); // 1 //Order Operators //use OrderBy extension operator to sort oldPhotos = list.OrderBy(p => p.year); //2002, 2003, 2005, 2007 foreach (var photo in oldPhotos) { Console.Out.WriteLine("photo = {0}", photo); } oldPhotos = list.OrderByDescending(p => p.year); //2007, 2005, 2003, 2002 foreach (var photo in oldPhotos) { Console.Out.WriteLine("photo = {0}", photo); } //Aggregate Operators var number = list.Count(p => p.year > 2003); //sums the length of all the photographer's names. Did I mention this is a contrived example? number = list.Sum(p => p.photographer.Length); Console.Out.WriteLine("number = {0}", number); //16 //find shortest subject length number = list.Min(p => p.subject.Length); Console.Out.WriteLine("number = {0}", number); //4 //using "Select" to create new anonymous classes var photos = list.Where(p => p.year < 2005).Select(p => new {p.photographer, p.year}); foreach (var photo in photos) { Console.Out.WriteLine("photo = {0}", photo); //photo = { photographer = Frank, year = 2002 }... } //write all photos to console list.ForEach(p => Console.WriteLine(p)); //same as above but more concise, and a little spooky list.ForEach(Console.WriteLine); Console.In.ReadLine(); } } }
- How to find prime numbers with LINQ
public static bool IsPrime(int n) { return Enumerable.Range(2, (int) Math.Sqrt(n) - 1).All(divisor => n%divisor != 0); }
- Create your own LINQ Query Operators
Below is an example of extending the IEnumerable<T> class to include an operator that returns an IEnumerable<T> of every other item.
using System; using System.Collections.Generic; using System.Linq; namespace Practice { class EveryOtherExample { private static void Main() { IEnumerable<int> ints = Enumerable.Range(0,7); var everyOther = ints.EveryOther(); //call our new Query Operator everyOther.ToList().ForEach(Console.Out.Write);// writes 0246 var letters = Enumerable.Range(0, 25).Select(i => (char) ((int) 'a' + i));//create the alphabet letters.EveryOther().ToList().ForEach(Console.Out.Write); //write out everyother: acegikmoqsuwy Console.Out.Write("\r\npress return to exit."); Console.In.ReadLine(); } } // let's create our own (non)Standard Query Operator /// <summary> /// returns every other item starting with the first item /// </summary> public static class MyEveryOtherExtensionMethod { public static IEnumerable<T> EveryOther<T>(this IEnumerable<T> source) { return source.Where((n, i) => i%2 == 0); } } }
- Deferred Queries
Many of the standard query operators do not execute immediately, but wait until needed. In the example below "abc.Intersect(cdef)" is not executed until needed for printing. It is re-evaluted when needed again.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class Deferred { private static void Main() { List<string> abc = new List<string> { "a", "b", "c" }; List<string> cdef = new List<string> { "c","d","e","f"}; IEnumerable intersection = abc.Intersect(cdef); //deferred execution cdef[3] = "a"; foreach (var letter in intersection) //now the IEnumerable intersection is done { Console.Out.WriteLine("letter = " + letter); //writes a,c } cdef[2] = "b"; //Getting the Enumerator for "intersection" forces it to start to reconstruct the list again foreach (var letter in intersection) { Console.Out.WriteLine("letter = " + letter); //writes a,b,c } Console.Out.Write("press return to exit."); Console.In.ReadLine(); } } }
- Explicit Interfaces
Sometimes different interfaces have the same method signatures. To specify a specific interface, preface the method with the name of the interface followed by a ".". When calling the method, the object must be cast as that interface type.
using System; namespace PlayingAround { class ExplicitInterface { interface IFlute { void Play(); } interface IGuitar { void Play(); } class Musician : IFlute, IGuitar { void IFlute.Play() { Console.WriteLine("Playing Flute"); } void IGuitar.Play() { Console.WriteLine("Playing Guitar"); } } public static void Main(string[] args) { Console.WriteLine("hello"); Musician muscian = new Musician(); //muscian.Play(); error //with Explicit Interfaces you must declare which interface is being called ((IFlute)muscian).Play(); Console.ReadLine(); } } }
- Creating an Xml document with Linq
(namespace omitted for clarity)
using System; using System.Xml.Linq; namespace PlayingAround { /// <summary> /// creates an xml document with nested elements /// </summary> class XmlCreate { private static void Main() { XElement books = new XElement("books", new XElement("book", new XAttribute("title","Peopleware"), new XElement("author","DeMarco and Lister")), new XElement("book", new XAttribute("title", "Agile and Iterative Development"), new XElement("author", "Craig Larman"))); Console.Out.WriteLine("books.ToString() = \r\n" + books); Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
Produces:
books.ToString() = <books> <book title="Peopleware"> <author>DeMarco and Lister</author> </book> <book title="Agile and Iterative Development"> <author>Craig Larman</author> </book> </books> press return to exit.
- Using LINQ to search xml files
We use "var" as the type of "demarco_books" becasue the real type reported by "GetType()" is quite a mouthful, "demarco_books.GetType() = System.Linq.Enumerable+<WhereIterator>d__0`1[System.Xml.Linq.XElement]"
using System; using System.IO; using System.Linq; using System.Xml.Linq; namespace PlayingAround { class LinqXml { private static void Main() { XElement books = XElement.Parse(File.ReadAllText(@"..\..\books.xml")); IEnumerable<XElement> demarco_books = books.Elements("book").Where(book => book.Element("author").ToString().Contains("DeMarco")); demarco_books.ToList().ForEach(b => Console.Out.WriteLine("DeMarco books: " + b.Attribute("title"))); Console.Out.Write("done."); Console.In.ReadLine(); } } }
Given this xml file
<books> <book title="Peopleware"> <author>DeMarco and Lister</author> </book> <book title="Agile and Iterative Development"> <author>Craig Larman</author> </book> <book title="Design Patterns"> <author>Gamma, Helm, Johnson, Blissides</author> </book> <book title="Software Creativity 2.0"> <author>Robert L Glass and Tom DeMarco</author> </book> </books>
The program produces:
DeMarco books: title="Peopleware" DeMarco books: title="Software Creativity 2.0" done.
- Using LINQ to sort objects
using System; using System.Linq; namespace PlayingAround { class LinqArray { private static void Main() { string[] presidents = {"Washington", "Madison", "Lincoln"}; //let's sort the array var sorted = presidents.OrderBy(p => p); sorted.ToList().ForEach(p => Console.Out.WriteLine(p)); Console.Out.Write("press return to exit."); Console.In.ReadLine(); } } }
- Using Legacy Collections
C# offers a few ways to use older collections like ArrayList with LINQ. Cast() will try to cast all elements to the desired generic type. In the example below Cast() is successful in casting "12" to a string, so the number of elements is 5. OfType() will pluck out of the original Arraylist all the correct types, so its count is only 4. If we uncomment the line adding "new object()", which cannot be cast to a string, the Cast() method will throw an InvalidCastException, but the OfType() method will just ignore it.
Some people will recommend using OfType() because it will not throw an exception when encountering an unexpected type of object. I usually prefer Cast() because I want to know up front during testing if any odd type has wandered into my collection. The only time to use OfType() is when you really expect to find mixed object types in your collection.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace PlayingAround { class Legacy { private static void Main() { ArrayList arrayList = new ArrayList(); arrayList.Add("up"); arrayList.Add("down"); arrayList.Add("charm"); arrayList.Add("strange"); arrayList.Add(12); //arrayList.Add(new object()); //will cause exception in Cast() IEnumerable<string> quarks = arrayList.Cast<string>().OrderByDescending(q => q); Console.Out.WriteLine("Number of elements = " + quarks.Count()); //5 quarks = arrayList.OfType<string>().OrderByDescending(q => q); Console.Out.WriteLine("Number of elements = " + quarks.Count()); //4 Console.Out.Write("press return to exit."); Console.In.ReadLine(); } } }
- Projection
Projection takes a series of one type of object and creates a series of another type.
using System; using System.Collections.Generic; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { public class Book { public string author; public string title; public string rating; } class Projection { private static void Main() { List<Book> books = new List<Book> { new Book {author = "Demarco", title = "PeopleWare", rating = "Good"}, new Book {author="Tolkien", title="The Hobbit", rating="Super"}, new Book {author="Pressfield", title="Gates of Fire", rating="Super"} }; //authorAndTitle has some machine-generated mangled type name var authorAndTitle = books.Select(b => new {b.author, b.title}); //projection authorAndTitle.ToList().ForEach(Console.WriteLine); /* prints: { author = Demarco, title = PeopleWare } { author = Tolkien, title = The Hobbit } { author = Pressfield, title = Gates of Fire } */ Console.Out.WriteLine("authorAndTitle type = " + authorAndTitle.GetType()); /* prints "authorAndTitle type = System.Linq.Enumerable+<SelectIterator>d__d`2[PlayingAround.Book,<>f__AnonymousType1`2[System.String,System.String]]" */ Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
- NonDeferred Conversion Methods
ToArray, ToList, ToDictionary, and ToLookup
using System; using System.Collections.Generic; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { public class BookItem { public string author; public string title; public string rating; } class NonDeferred { private static void Main() { List<BookItem> books = new List<BookItem> { new BookItem {author = "Demarco", title = "PeopleWare", rating = "Good"}, new BookItem {author= "Tolkien", title= "The Hobbit", rating = "Super"}, new BookItem {author= "Pressfield", title = "Gates of Fire", rating = "Super"} }; //convert to an array BookItem[] bookItems = books.ToArray(); //convert to list List<BookItem> bookList = books.ToList(); //now, finally something interesting, the dictionary, (one key to one object) Dictionary<string, BookItem> dict = books.ToDictionary(b => b.author); Console.Out.WriteLine(dict["Tolkien"].title); //writes: The Hobbit //now for something even more interesting, a Lookup (one key, many objects) ILookup<string, BookItem> bookLookup = books.ToLookup(b => b.rating); IEnumerable<BookItem> superBooks = bookLookup["Super"]; foreach (var book in superBooks) { Console.WriteLine("A super book is "+book.title); //writes: The Hobbit, Gates of Fire } Console.Out.Write("press return to exit.");Console.In.ReadLine(); } } }
- Search a string for any occurance of a substring in a List
If you have a string and want to know if any of a list of strings are contained in it:
string text = ... text I'm searching ... List<string> keywordList = ... words I'm looking to find in "text" ... result = keywordList.Any(s => text.Contains(s));
- Aggregate, the "Reduce" function in LINQ
using System; using System.Linq; //need this to get creamy goodness of IEnumerable<T> extension methods namespace PlayingAround { class Aggregate { private static void Main() { int[] numbers = {1, 2, 3, 4, 5, 6}; //Aggregate performs a function on each of the elements and carries the result //forward to the next element (Called a "Reduce" operation) int sumOfSquares = numbers.Aggregate(0, (sum,n) => n*n + sum); Console.Out.WriteLine("sumOfSquares = " + sumOfSquares); //writes: 91 //concatenate all the strings together string allwords = words.Aggregate("", (temp, word) => temp + word); Console.Out.WriteLine("allwords = {0}", allwords); //writes: onetwothree Console.ReadKey(); } } }
- How to do a Selection sort with a generic list
Selection Sort has a BigO on n2, so it's not our first choice, but it demonstrates working with generics.
using System; using System.Collections.Generic; using System.Linq; namespace Practice { class SelectionSort { static void Main() { List<int> nums = new List<int>{3, 6, 2, 7, 0, 9, 8, 2, 1}; MySort<int>(nums); nums.ToList().ForEach(Console.Write);//012236789 var names = new List<string>{ "Rack", "Shack", "Bennie", "Dan" }; MySort<string>(names); names.ToList().ForEach(Console.Write);//BennieDanRackShack Console.Write("Press 'Enter' to exit.");Console.In.ReadLine(); } public static List<T> MySort<T>(List<T> t) where T : IComparable<T> { //sweep through list and find min and max values and first and last unsorted elements for (int i = 0; i < t.Count/2; i++)//only go half way since we replace 2 each time { int indexOfMax = i; int indexOfMin = i; //find max and min in remaining list for (int j = i; j < t.Count - i; j++) { if (t[j].CompareTo(t[indexOfMax]) < 0) indexOfMax = j; if (t[j].CompareTo(t[indexOfMin]) > 0) indexOfMin = j; } SwapValues<T>(t, i, indexOfMax); SwapValues<T>(t, t.Count - i -1, indexOfMin); } return t; } private static void SwapValues<T>(List<T> t, int here, int there) where T : IComparable<T> { T temp = t[here]; t[here] = t[there]; t[there] = temp; } } }
- Example using Standard Query Operators