0% found this document useful (0 votes)
15 views

Lecture 09 - LINQ to Objects

Uploaded by

ahihihi.0602
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views

Lecture 09 - LINQ to Objects

Uploaded by

ahihihi.0602
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 43

C# 6 and the .NET 4.

6 Framework

Chapter 12 – LINQ to Objects


Outline
• LINQ-Specific Programming Constructs
• Understanding the Role of LINQ
• Applying LINQ Queries to Primitive Arrays
• Returning the Result of LINQ Query
• Applying LINQ Queries to Collection Objects
• Investigating the C# LINQ Query Operations
Introduction
• Your program will certainly need to access some form of data as it
executes
– Data can be found in numerous locations, including XML files,
relational databases, in-memory collections, and primitive arrays.
– Historically speaking, based on the location of said data, programmers
needed to make use of different and unrelated APIs
• The Language Integrated Query (LINQ) technology set
– Introduced initially in .NET 3.5
– Provides a concise, symmetrical, and strongly typed manner to access
a wide variety of data stores
• LINQ provides query operators and query expressions
– Allow you to define statements that will interrogate a data source to
yield the requested result set
LINQ-SPECIFIC PROGRAMMING
CONSTRUCTS
Introduction
• LINQ
– Strongly typed query language, embedded directly into the grammar
of C#
– You can build any number of expressions that have a look and feel
similar to that of a database SQL query
– It can be applied to any number of data stores, including stores that
have nothing to do with a literal relational database
• The C# language uses the following core LINQ-centric features:
– Implicitly typed local variables
– Object/collection initialization syntax
– Lambda expressions
– Extension methods
– Anonymous types
Extension Methods
• C# extension methods
– Allow you to tack on new functionality to existing classes
without the need to subclass
– Allow you to add new functionality to sealed classes and
structures, which could never be subclassed in the first place
• Author an extension method
– The first parameter is qualified with the this keyword and marks
the type being extended
– Extension methods must always be defined within a static class
and must, therefore, also be declared using the static keyword
• To use an extension method
– An application must import the namespace defining the
extension
Object Extensions
using System;
using System.Reflection;
namespace MyExtensions
{
static class ObjectExtensions
{
//Define an extension method to System.Object
public static void DisplayDefiningAssembly(this object obj)
{
Console.WriteLine("{0} lives here:\n\t->{1}", obj.GetType().Name, Assembly.GetAssembly(obj.GetType()));
}
}
}

using System;
namespace MyExtensions
{
class MainClass
{
public static void Main(string[] args)
{
//Since everything extends System.Object, all classes and structures can use this
int myInt = 12345678;
myInt.DisplayDefiningAssembly();
}
}
}
Anonymous Types
• Anonymous types feature
– To quickly model the "shape" of data
– Is based on a supplied set of name-value pairs
– To allow the compiler to generate a new class definition at compile
time
• To define an anonymous type
– Declare an implicitly typed variable
– Specify the data's shape using object initialization syntax
• LINQ makes frequent use of anonymous types
– To project new forms of data on the fly
• Example
– Assume you have a collection of Person objects
– LINQ to obtain information on the age and SSN of each
– LINQ allows compiler to generate a new anonymous type for this info
Anonymous Types
using System;
namespace AnonymousTypes
{
class MainClass
{
public static void Main(string[] args)
{
var purchaseItem = new
{
TimeBought = DateTime.Now,
ItemBought = new { Color = "Red", Make = "Saab", CurrentSpeed = 55 }
};
var ItemBought = purchaseItem.ItemBought;
Console.WriteLine("Color: {0}, Make: {1}, CurrentSpeed: {2}, Bought at: {3}",
ItemBought.Color, ItemBought.Make, ItemBought.CurrentSpeed, purchaseItem.TimeBought);
}
}
}
Roles of LINQ
• The LINQ
– Provides a consistent, symmetrical manner to obtain and manipulate
"data”
– The data can come from different sources
– Can be used to create query expressions
– These are based on numerous query operators designed to look and
feel similar (but not quite identical) to a SQL expression
• LINQ terminologies:
– LINQ to Objects: applying LINQ queries to arrays and collections
– LINQ to XML: using LINQ to manipulate and query XML documents
– LINQ to DataSet: applying LINQ queries to ADO.NET DataSet objects
– LINQ to Entities: using queries within the ADO.NET Entity Framework
– Parallel LINQ (aka PLINQ): Parallel processing of data returned from
LINQ
LINQ Expressions Are Strongly Typed
• It is also important to point out that a LINQ
query expression is strongly typed
– Therefore, the C# compiler will keep you honest
and make sure that these expressions are
syntactically well-formed.
– Tools such as Visual Studio can use metadata for
useful features such as IntelliSense,
autocompletion, and so forth.
APPLYING LINQ QUERIES TO
PRIMITIVE ARRAYS
First LINQ to Array Query
using System;
using System.Linq;
using System.Collections.Generic;
namespace LinqOverArray
{
class MainClass
{
public static void QueryOverStrings()
{
// Assume we have an array of strings.
string[] currentVideoGames = {"Morrowind", "Uncharted 2", "Fallout 3", "Daxter", "System Shock 2"};
// Build a query expression to find the items in the array that have an embedded space. Then order the result
IEnumerable<string> subset = from g in currentVideoGames
where g.Contains(" ")
orderby g
select g;
// Print out the results.
foreach (string s in subset)
Console.WriteLine("Item: {0}", s);
}
public static void Main(string[] args)
{
Console.WriteLine("************** Fun with LINQ **************");
QueryOverStrings();
Console.ReadLine();
}
}
}
Once Again, Without LINQ
using System;
using System.Collections.Generic;
namespace LinqOverArray
{
class MainClass
{
static void QueryOverStringsLongHand()
{
// Assume we have an array of strings.
string[] currentVideoGames = {"Morrowind", "Uncharted 2", "Fallout 3", "Daxter", "System Shock 2"};

string[] gamesWithSpaces = new string[5];

for (int i = 0; i < currentVideoGames.Length; i++)


{
if (currentVideoGames[i].Contains(" "))
gamesWithSpaces[i] = currentVideoGames[i];
}
// Now sort them.
Array.Sort(gamesWithSpaces);
// Print out the results.
foreach (string s in gamesWithSpaces)
{
if (s != null)
Console.WriteLine("Item: {0}", s);
}
Console.WriteLine();
}
public static void Main(string[] args)
{
Console.WriteLine("************** Fun with LINQ **************");
QueryOverStringsLongHand();
Console.ReadLine();
}
}
}
LINQ and Implicitly Typed Local
Variables
using System;
using System.Linq;
using System.Collections.Generic;
namespace LinqOverArray
{
class MainClass As a rule of thumb, you will always
{
static void QueryOverInts() want to make use of implicit typing
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 }; when capturing the results of a LINQ
// Use implicit typing here... query. Just remember, however, that (in
var subset = from i in numbers where i < 10 select i;
a vast majority of cases) the real return
// ...and here.
foreach (var i in subset)
value is a type implementing the
}
Console.WriteLine("Item: {0} ", i); generic IEnumerable<T> interface.
public static void Main(string[] args)
{
Console.WriteLine("************** Fun with LINQ **************");
QueryOverInts();
Console.ReadLine();
}
}
}
LINQ and Extension Methods
• LINQ query expressions can be used to iterate over
data containers that implement the generic
IEnumerable<T> interface
– However, the .NET System.Array class type does not
implement this contract
– The System.Array type does not seem to implement the
correct infrastructure for query expressions
– System.Array gains the required functionality of this type
via the static System.Linq.Enumerable class type
• This utility class defines a good number of generic
extension methods
– E.g., Aggregate<T>(), First<T>(), Max<T>(), etc.
The Role of Deferred Execution
• LINQ query expressions are not actually
evaluated until you iterate over the sequence
– This is termed deferred execution
• The benefit of this approach is
– We can apply the same LINQ query multiple times
to the same container and still obtain the latest
and greatest results
The Role of Deferred Execution
using System;
using System.Linq;
using System.Collections.Generic;
namespace LinqOverArray
{
class MainClass
{
static void QueryOverInts()
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Get numbers less than ten.
var subset = from i in numbers where i < 10 select i;
Console.WriteLine("Evaluate the result");
foreach (var i in subset)
Console.WriteLine("{0} < 10", i);
Console.WriteLine();
Console.WriteLine("Change some data");
numbers[0] = 4;
Console.WriteLine("Evaluate again, without having to run the query");
foreach (var j in subset)
Console.WriteLine("{0} < 10", j);
Console.WriteLine();
}
public static void Main(string[] args)
{
Console.WriteLine("************** Fun with LINQ **************");
QueryOverInts();
Console.ReadLine();
}
}
}
The Role of Immediate Execution
• When you need to evaluate a LINQ expression
from outside the confines of foreach logic, you
are able to call any number of extension
methods defined by the Enumerable type
such as ToArray<T>(),
ToDictionary<TSource,TKey>(), and
ToList<T>(). These methods will cause a LINQ
query to execute at the exact moment you call
them, to obtain a snapshot of the data
The Role of Immediate Execution
using System;
using System.Linq;
using System.Collections.Generic;
namespace LinqOverArray
{
class MainClass
{
static void ImmediateExecution()
C# compiler can unambiguously
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
determine the type parameter of a
generic, you are not required to specify
// Get data RIGHT NOW as int[].
int[] subsetAsIntArray = the type parameter. Thus, you could
(from i in numbers where i < 10 select i).ToArray<int>();
also call ToArray<T>() (or ToList<T>() for
// Get data RIGHT NOW as List<int>.
List<int> subsetAsListOfInts = that matter)
(from i in numbers where i < 10 select i).ToList<int>();
}
public static void Main(string[] args)
{
Console.WriteLine("************** Fun with LINQ **************");
ImmediateExecution();
Console.ReadLine();
}
}
}
Applying LINQ Queries to Collection
Objects
public class Car
{
public string PetName{get;set;}
public string Color{get;set;}
public int Speed{get;set;}
public string Make{get;set;}
}
Applying LINQ Queries to Collection
Objects
public static void Main(string[] args)
{
Console.WriteLine("************ LINQ over generic collections *************");
List<Car> myCars = new List<Car>()
{
new Car{PetName = "Henry", Color="Silver", Speed=100, Make="BMV"},
new Car{PetName = "Daisy", Color="Tan", Speed=90, Make="BMV"},
new Car{PetName = "Mary", Color="Black", Speed=55, Make="VW"},
new Car{PetName = "Clunker", Color="Rust", Speed=5, Make="Yugo"},
new Car{PetName = "Melvin", Color="White", Speed=43, Make="Ford"}
};
GetFastCars(myCars);
GetFastBMVs(myCars);
Console.ReadLine();
}
Accessing Contained Subobjects
public static void GetFastCars(List<Car> myCars)
{
//select cars with speed >= 55
var fastCars = from c in myCars
where c.Speed > 55
select c;
foreach (var item in fastCars)
{
System.Console.WriteLine($"{item.PetName} is going too fast");
}
}
public static void GetFastBMVs(List<Car> myCars){
//select bmv cars that go faster than 55km
var fastBMVs = from c in myCars
where c.Speed > 55 && c.Make.Equals("BMV")
select c;
foreach (var item in fastBMVs)
{
System.Console.WriteLine($"{item.PetName} is going too fast.");
}
}
Applying LINQ Queries to Nongeneric
Collections
public static void Main(string[] args)
{
Console.WriteLine("********* LINQ over ArrayList *************");
ArrayList myCars = new ArrayList(){
new Car{PetName = "Henry", Color="Silver", Speed=100, Make="BMV"},
new Car{PetName = "Daisy", Color="Tan", Speed=90, Make="BMV"},
new Car{PetName = "Mary", Color="Black", Speed=55, Make="VW"},
new Car{PetName = "Clunker", Color="Rust", Speed=5, Make="Yugo"},
new Car{PetName = "Melvin", Color="Silver", Speed=43, Make="Ford"}
};
//Transform ArrayList into an INumerable<T> compatible type.
var myCarsEnum = myCars.OfType<Car>();
//Create a query expression targeting the compatible type. Filtering Data Using OfType<T>( )
var fastCars = from c in myCarsEnum where c.Speed > 55 select c;
foreach (var item in fastCars)
{
System.Console.WriteLine($"{item.PetName} is going too fast!");
}
}
Investigating the C# LINQ Query
Operators
ProductInfo.cs
using System;
namespace FunWithLinqExpressions
{
public class ProductInfo
{
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public int NumberInStock { get; set; } = 0;
public override string ToString()
{
return string.Format($"Name={Name}, Description={Description}, NumberInStock={NumberInStock}");
}
}
}
Program.cs
using System;
namespace FunWithLinqExpressions
{
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("***** Fun with Query Expressions *****\n");

// This array will be the basis of our testing...


ProductInfo[] itemsInStock = new[] {
new ProductInfo{ Name = "Mac's Coffee",
Description = "Coffee with TEETH", NumberInStock = 24},
new ProductInfo{ Name = "Milk Maid Milk",
Description = "Milk cow's love", NumberInStock = 100},
new ProductInfo{ Name = "Pure Silk Tofu",
Description = "Bland as Possible", NumberInStock = 120},
new ProductInfo{ Name = "Cruchy Pops", Description = "Cheezy, peppery goodness", NumberInStock = 2},
new ProductInfo{ Name = "RipOff Water",
Description = "From the tap to your wallet", NumberInStock = 100},
new ProductInfo{ Name = "Classic Valpo Pizza",
Description = "Everyone loves pizza!", NumberInStock = 73}};

// We will call various methods here!


Console.ReadLine();
}
}
}
Basic Selection Syntax
static void SelectEverything(ProductInfo[] products)
{
// Get everything!
Console.WriteLine("All product details:");
var allProducts = from p in products select p; Select everything (not very
foreach (var prod in allProducts) useful)
{
Console.WriteLine(prod.ToString());
}
}
static void ListProductNames(ProductInfo[] products)
{
// Now get only the names of the products.
Console.WriteLine("Only product names:");
var names = from p in products select p.Name; Project all names
foreach (var n in names)
{
Console.WriteLine("Name: {0}", n);
}
}
Obtaining Subsets of Data
static void GetOverstock(ProductInfo[] products)
{
Console.WriteLine("The overstock items!");

// Get only the items where we have more than


// 25 in stock.
var overstock = from p in products where p.NumberInStock > 25 select p;

foreach (ProductInfo p in overstock)


{
Console.WriteLine(p.ToString());
} it is permissible to make use of
}
static void GetPizzaOverStock(ProductInfo[] products){
any valid C# operators to build
// Get BMWs going at least 100 mph. complex expressions
var onlyPizzaOverStock = from p in products
where p.NumberInStock > 25 && p.Name.ToLower().Contains("pizza") select p;
foreach (ProductInfo p in onlyPizzaOverStock)
{
Console.WriteLine(p);
}
}
Sorting the results
static void AlphabetizeProductNames(ProductInfo[] products)
{
//By default name is sorted ascendingly
var subset = from p in products orderby p.Name select p;
//Sort descendingly It is also possible to project new
//var subset = from p in products orderby p.Name descending select p; of data from an existing
forms
Console.WriteLine("Ordered by Name:");
foreach (var p in subset)
data source. This feature is
{ making use of implicit type (we
Console.WriteLine(p.ToString()); must use var here)
}
}

Note that var is not used as


return type. So this will not
compile
Returning Projected New Data Types
// Return value is now an Array.
static Array GetProjectedSubset(ProductInfo[] products)
{
var nameDesc = from p in products select new { p.Name, p.Description };

// Map set of anonymous objects to an Array object. If we need to return anonymous


return nameDesc.ToArray(); type, we need to use Array type
}

public static void Main(string[] args)


{

//GetPizzaOverStock(itemsInStock); Now we can use the object this way
Array objs = GetProjectedSubset(itemsInStock); (can’t cast to any type).
foreach (object o in objs) You can call to ToString() method
{
Console.WriteLine(o); // Calls ToString() on each anonymous object.
}
Console.ReadLine();
}
Obtaining Counts Using Enumerable
static void GetCountFromQuery()
{
string[] currentVideoGames = {"Morrowind", "Uncharted 2",
"Fallout 3", "Daxter", "System Shock 2"};
Use Count() extension method
// Get count from the query. of the Enumerable class
int numb =
(from g in currentVideoGames where g.Length > 6 select g).Count();

// Print out the number of items.


Console.WriteLine("{0} items honor the LINQ query.", numb);
}
Reversing Result Sets
static void ReverseEverything(ProductInfo[] products)
{
Console.WriteLine("Product in reverse:");
var allProducts = from p in products select p; using the Reverse<>()
foreach (var prod in allProducts.Reverse()) extension method of the
{
Console.WriteLine(prod.ToString());
Enumerable class
}
}
Sorting Expressions
static void AlphabetizeProductNames(ProductInfo[] products)
{
//By default name is sorted ascendingly
var subset = from p in products orderby p.Name select p;
//Sort descendingly
//var subset = from p in products orderby p.Name descending select p;
Console.WriteLine("Ordered by Name:");
foreach (var p in subset)
{ If you want to get the items in descending
Console.WriteLine(p.ToString());
} order, you can do so via the descending
} operator.
LINQ AS A BETTER VENN
DIAGRAMMING TOOL
Difference
static void DisplayDiff()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec" };

var carDiff = (from c in myCars select c) Difference with Except extension method
.Except(from c2 in yourCars select c2);

Console.WriteLine("Here is what you don't have, but I do:");


foreach (string s in carDiff)
Console.WriteLine(s); // Prints Yugo.
}
Intersection
static void DisplayIntersection()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec" };

// Get the common members. Intersection with Intersect() extension


var carIntersect = (from c in myCars select c) method
.Intersect(from c2 in yourCars select c2);

Console.WriteLine("Here is what we have in common:");


foreach (string s in carIntersect)
Console.WriteLine(s); // Prints Aztec and BMW.
}
Union
static void DisplayUnion()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec" };

// Get the union of these containers. Union with Union() extension method
var carUnion = (from c in myCars select c)
.Union(from c2 in yourCars select c2);

Console.WriteLine("Here is everything:");
foreach (string s in carUnion)
Console.WriteLine(s); // Prints all common members.
}
Concatenation
static void DisplayConcat()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec" };

var carConcat = (from c in myCars select c) Concatenation with Concat() extension


.Concat(from c2 in yourCars select c2); method
// Prints:
// Yugo Aztec BMW BMW Saab Aztec.
foreach (string s in carConcat)
Console.WriteLine(s);
}
Removing Duplicates
static void DisplayConcatNoDups()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec" };

var carConcat = (from c in myCars select c) Remove duplicates with Distinct() extension
.Concat(from c2 in yourCars select c2); method
// Prints:
// Yugo Aztec BMW Saab Aztec.
foreach (string s in carConcat.Distinct())
Console.WriteLine(s);
}
LINQ Aggregation Operations
static void AggregateOps()
{
double[] winterTemps = { 2.0, -21.3, 8, -4, 0, 8.2 };

// Various aggregation examples.


Console.WriteLine("Max temp: {0}",
(from t in winterTemps select t).Max());
Aggregations with extension methods:
Console.WriteLine("Min temp: {0}", - Max()
(from t in winterTemps select t).Min());
- Min()
Console.WriteLine("Average temp: {0}", - Average()
(from t in winterTemps select t).Average()); - Sum()
Console.WriteLine("Sum of all temps: {0}", - Count() //learnt previously
(from t in winterTemps select t).Sum());
}
Summary
• LINQ-Specific Programming Constructs
• Understanding the Role of LINQ
• Applying LINQ Queries to Primitive Arrays
• Returning the Result of LINQ Query
• Applying LINQ Queries to Collection Objects
• Investigating the C# LINQ Query Operations
References
Troelsen, A. & Japikse, P., 2015. C# 6 and the
.NET 4.6 Framework. 7th ed. Apress.

You might also like