Last Updated:.

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.

  1. 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);
            }
        }
    }
    
  2. 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;
            }
        }
    }
    
  3. 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();
            }
        }
    }
    
  4. 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();
        }
    }
    
  5. 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);
    
  6. 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();
        }
    }
    
  7. 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);
    
  8. 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"};
    
  9. 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.

  10. 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.");
            }
        }
    
  11. 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.

    1. Types of LINQ queries
      1. Sequence to Sequence - IEnumerable<TSource> to IEnumerable<TSource>
        1. Filtering - Where, Take, TakeWhile, Skip, SkipWhile, Distinct
        2. Projecting - Select, SelectMany
        3. Joining - Join, GroupJoin, Zip
        4. Ordering - OrderBy, ThenBy, Reverse
        5. Grouping - GroupBy
        6. Set Operators - Concat, Union, Intersect, Except
        7. Conversion methods - OfType, Cast, ToArray, ToList, ToDictionary, ToLookup, AsEnumerable, AsQueryable
      2. Sequence to Element or value - IEnumerable<TSource> to <TSource>
        1. Search operators - First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty
        2. Aggregation methods - Aggregate, Average, Count, LongCount, Sum, Max, Min
        3. Quantifiers or Logical - All, Any, Contains, SequenceEqual
      3. void to Sequence - Void to IEnumerable<TSource>
        1. Generation methods - Empty, Range, Repeat
    2. 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();
              }
          }
      }
      
    3. 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();
              }
          }
      }
      
    4. 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();
              }
          }
      }
      
    5. 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.
      
    6. 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();
              }
          }
      }
      
    7. GroupBy

      GroupBy does the SQL equivalent of, wait for it ..., "Group By". It takes an IEnumerable<T> and creates IEnumerable<IGrouping<string,string>>.

    8. 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 generic = nonGeneric.Cast();
      generic.ToList().ForEach(Console.WriteLine);
      
    9. 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();
              }
          }
      }
    10. 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();
              }
          }
      }
  12. 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()".

    1. 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();
              }
          }
      }
      
    2. 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);
      }
      
    3. 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);
              }
          }
      }
      
    4. 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();
                  
              }
          }
      }
    5. 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();
              }
          }
      }
      
    6. 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.
      
    7. 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.
    8. 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();
              }
          }
      }
    9. 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();
              }
          }
      }
    10. 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();
              }
          }
      }
    11. 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();
              }
          }
      }
    12. 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));
      
    13. 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();
          }
        }
      }
      
      
    14. How to do a Selection sort with a generic list

      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;
              }
          }
      }
      
Go to Home page. Kindly report errors, typos, or misspellings here.