Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 185 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
185
Dung lượng
3,29 MB
Nội dung
Part I: The C# Language 310 Standard Query Operators Description Where OfType < TResult > Filtering operators define a restriction to the elements returned. With the Where query operator you can use a predicate, for exam- ple, defined by a Lambda expression that returns a bool. OfType < TResult > filters the elements based on the type and returns only the elements of the type TResult . Select SelectMany Projection operators are used to transform an object into a new object of a different type. Select and SelectMany define a pro- jection to select values of the result based on a selector function. OrderBy ThenBy OrderByDescending ThenByDescending Reverse Sorting operators change the order of elements returned. OrderBy sorts values in ascending order; OrderByDescending sorts val- ues in descending order. ThenBy and ThenByDescending opera- tors are used for a secondary sort if the first sort gives similar results. Reverse reverses the elements in the collection. Join GroupJoin Join operators are used to combine collections that might not be directly related to each other. With the Join operator a join of two collections based on key selector functions can be done. This is simi- lar to the JOIN you know from SQL. The GroupJoin operator joins two collections and groups the results. GroupBy Grouping operators put the data into groups. The GroupBy opera- tor groups elements with a common key. Any All Contains Quantifier operators return a Boolean value if elements of the sequence satisfy a specific condition. Any , All , and Contains are quantifier operators. Any determines if any element in the collec- tion satisfies a predicate function; All determines if all elements in the collection satisfy a predicate. Contains checks whether a specific element is in the collection. These operators return a Boolean value. Take Skip TakeWhile SkipWhile Partitioning operators return a subset of the collection. Take , Skip , TakeWhile , and SkipWhile are partitioning operators. With these, you get a partial result. With Take , you have to specify the number of elements to take from the collection; Skip ignores the specified number of elements and takes the rest. TakeWhile takes the ele- ments as long as a condition is true. Distinct Union Intersect Except Set operators return a collection set. Distinct removes dupli- cates from a collection. With the exception of Distinct , the other set operators require two collections. Union returns unique ele- ments that appear in either of the two collections. Intersect returns elements that appear in both collections. Except returns elements that appear in just one collection. c11.indd 310c11.indd 310 2/19/08 5:10:47 PM2/19/08 5:10:47 PM Chapter 11: Language Integrated Query 311 Standard Query Operators Description First FirstOrDefault Last LastOrDefault ElementAt ElementAtOrDefault Single SingleOrDefault Element operators return just one element. First returns the first element that satisfies a condition. FirstOrDefault is similar to First , but it returns a default value of the type if the element is not found. Last returns the last element that satisfies a condition. With ElementAt , you specify the position of the element to return. Single returns only the one element that satisfies a condi- tion. If more than one element satisfies the condition, an excep- tion is thrown. Count Sum Min Max Average Aggregate Aggregate operators compute a single value from a collection. With aggregate operators, you can get the sum of all values, the number of all elements, the element with the lowest or highest value, an average number, and so on. ToArray AsEnumerable ToList ToDictionary Cast < TResult > Conversion operators convert the collection to an array: IEnumerable , IList , IDictionary , and so on. Empty Range Repeat Generation operators return a new sequence. The collection is empty using the Empty operator, Range returns a sequence of numbers, and Repeat returns a collection with one repeated value. Following are examples of using these operators. Filtering Have a look at some examples for a query. With the where clause, you can combine multiple expressions; for example, get only the racers from Brazil and Austria who won more than 15 races. The result type of the expression passed to the where clause just needs to be of type bool: var racers = from r in Formula1.GetChampions() where r.Wins > 15 & & (r.Country == “Brazil” || r.Country == “Austria”) select r; foreach (var r in racers) { Console.WriteLine(“{0:A}”, r); } Starting the program with this LINQ query returns Niki Lauda, Nelson Piquet, and Ayrton Senna as shown: Niki Lauda, Austria, Starts: 173, Wins: 25 Nelson Piquet, Brazil, Starts: 204, Wins: 23 Ayrton Senna, Brazil, Starts: 161, Wins: 41 c11.indd 311c11.indd 311 2/19/08 5:10:47 PM2/19/08 5:10:47 PM Part I: The C# Language 312 Not all queries can be done with the LINQ query. Not all extension methods are mapped to LINQ query clauses. Advanced queries require using extension methods. To better understand complex queries with extension methods it ’ s good to see how simple queries are mapped. Using the extension methods Where() and Select() produces a query very similar to the LINQ query done before: var racers = Formula1.GetChampions(). Where(r = > r.Wins > 15 & & (r.Country == “Brazil” || r.Country == “Austria”)). Select(r = > r); Filtering with Index One example where you can ’ t use the LINQ query is an overload of the Where() method. With an overload of the Where() method you can a pass a second parameter that is the index. The index is a counter for every result returned from the filter. You can use the index within the expression to do some calculation based on the index. Here the index is used within the code that is called by the Where() extension method to return only racers whose last name starts with A if the index is even: var racers = Formula1.GetChampions(). Where((r, index) = > r.LastName.StartsWith(“A”) & & index % 2 != 0); foreach (var r in racers) { Console.WriteLine(“{0:A}”, r); } All the racers with last names beginning with the letter A are Alberto Ascari, Mario Andretti, and Fernando Alonso. Because Mario Andretti is positioned within an index that is odd, he is not in the result: Alberto Ascari, Italy; starts: 32, wins: 10 Fernando Alsonso, Spain; starts: 105, wins: 19 Type Filtering For filtering based on a type you can use the OfType() extension method. Here the array data contains both string and int objects. Using the extension method OfType() , passing the string class to the generic parameter returns only the strings from the collection: object[] data = { “one”, 2, 3, “four”, “five”, 6 }; var query = data.OfType < string > (); foreach (var s in query) { Console.WriteLine(s); } Running this code, the strings one, four, and five are displayed: one four five c11.indd 312c11.indd 312 2/19/08 5:10:48 PM2/19/08 5:10:48 PM Chapter 11: Language Integrated Query 313 Compound from If you need to do a filter based on a member of the object that itself is a sequence, you can use a compound from . The Racer class defines a property Cars where Cars is a string array. For a filter of all racers who were champions with a Ferrari, you can use the LINQ query as shown. The first from clause accesses the Racer objects returned from Formula1.GetChampions() . The second from clause accesses the Cars property of the Racer class to return all cars of type string . Next the cars are used with the where clause to filter only the racers who were champions with a Ferrari. var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == “Ferrari” orderby r.LastName select r.FirstName + “ “ + r.LastName; If you are curious about the result of this query, all Formula - 1 champions driving a Ferrari are: Alberto Ascari Juan Manuel Fangio Mike Hawthorn Phil Hill Niki Lauda Jody Scheckter Michael Schumacher John Surtees The C# compiler converts a compound from clause with a LINQ query to the SelectMany() extension method. SelectMany() can be used to iterate a sequence of a sequence. The overload of the SelectMany method that is used with the example is shown here: public static IEnumerable < TResult > SelectMany < TSource, TCollection, TResult > ( this IEnumerable < TSource > source, Func < TSource, IEnumerable < TCollection > > collectionSelector, Func < TSource, TCollection, TResult > resultSelector); The first parameter is the implicit parameter that receives the sequence of Racer objects from the GetChampions() method. The second parameter is the collectionSelector delegate where the inner sequence is defined. With the Lambda expression r = > r.Cars the collection of cars should be returned. The third parameter is a delegate that is now invoked for every car and receives the Racer and Car objects. The Lambda expression creates an anonymous type with a Racer and a Car property. As a result of this SelectMany() method the hierarchy of racers and cars is flattened and a collection of new objects of an anonymous type for every car is returned. This new collection is passed to the Where() method so that only the racers driving a Ferrari are filtered. Finally, the OrderBy() and Select() methods are invoked. var ferrariDrivers = Formula1.GetChampions(). SelectMany( r = > r.Cars, (r, c) = > new { Racer = r, Car = c }). Where(r = > r.Car == “Ferrari”). OrderBy(r = > r.Racer.LastName). Select(r = > r.Racer.FirstName + “ “ + r.Racer.LastName); c11.indd 313c11.indd 313 2/19/08 5:10:48 PM2/19/08 5:10:48 PM Part I: The C# Language 314 Resolving the generic SelectMany() method to the types that are used here, the types are resolved as follows. In this case the source is of type Racer , the filtered collection is a string array, and of course the name of the anonymous type that is returned is not known and shown here as TResult : public static IEnumerable < TResult > SelectMany < Racer, string, TResult > ( this IEnumerable < Racer > source, Func < Racer, IEnumerable < string > > collectionSelector, Func < Racer, string, TResult > resultSelector); Because the query was just converted from a LINQ query to extension methods, the result is the same as before. Sorting For sorting a sequence, the orderby clause was used already. Let ’ s review the example from before with the orderby descending clause. Here the racers are sorted based on the number of wins as specified by the key selector in a descending order: var racers = from r in Formula1.GetChampions() where r.Country == “Brazil” orderby r.Wins descending select r; The orderby clause is resolved to the OrderBy() method, and the orderby descending clause is resolved to the OrderBy Descending() method: var racers = Formula1.GetChampions(). Where(r = > r.Country == “Brazil”). OrderByDescending(r = > r.Wins). Select(r = > r); The OrderBy() and OrderByDescending() methods return IOrderedEnumerable < TSource > . This interface derives from the interface IEnumerable < TSource > but contains an additional method CreateOrderedEnumerable < TSource > () . This method is used for further ordering of the sequence. If two items are the same based on the key selector, ordering can continue with the ThenBy() and ThenByDescending() methods. These methods require an IOrderedEnumerable < TSource > to work on, but return this interface as well. So, you can add any number of ThenBy() and ThenByDescending() to sort the collection. Using the LINQ query you just have to add all the different keys (with commas) for sorting to the orderby clause. Here the sort of all racers is done first based on the country, next on the last name, and finally on the first name. The Take() extension method that is added to the result of the LINQ query is used to take just the first 10 results. var racers = (from r in Formula1.GetChampions() orderby r.Country, r.LastName, r.FirstName select r).Take(10); The sorted result is shown here: Argentina: Fangio, Juan Manuel Australia: Brabham, Jack Australia: Jones, Alan Austria: Lauda, Niki Austria: Rindt, Jochen Brazil: Fittipaldi, Emerson c11.indd 314c11.indd 314 2/19/08 5:10:48 PM2/19/08 5:10:48 PM Chapter 11: Language Integrated Query 315 Brazil: Piquet, Nelson Brazil: Senna, Ayrton Canada: Villeneuve, Jacques Finland: Hakkinen, Mika Doing the same with extension methods makes use of the OrderBy() and ThenBy() methods: var racers = Formula1.GetChampions(). OrderBy(r = > r.Country). ThenBy(r = > r.LastName). ThenBy(r = > r.FirstName). Take(10); Grouping To group query results based on a key value, the group clause can be used. Now the Formula - 1 champions should be grouped by the country, and the number of champions within a country should be listed. The clause group r by r.Country into g groups all the racers based on the Country property and defines a new identifier g that can be used later to access the group result information. The result from the group clause is ordered based on the extension method Count() that is applied on the group result, and if the count is the same the ordering is done based on the key, which is the country because this was the key used for grouping. The where clause filters the results based on groups that have at least two items, and the select clause creates an anonymous type with Country and Count properties. var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() > = 2 select new { Country = g.Key, Count = g.Count() }; foreach (var item in countries) { Console.WriteLine(“{0, -10} {1}”, item.Country, item.Count); } The result displays the collection of objects with the Country and Count property: UK 9 Brazil 3 Australia 2 Austria 2 Finland 2 Italy 2 USA 2 Doing the same with extension methods, the groupby clause is resolved to the GroupBy() method. What ’ s interesting with the declaration of the GroupBy() method is that it returns an enumeration of objects implementing the IGrouping interface. The IGrouping interface defines the Key property, so you can access the key of the group after defining the call to this method: public static IEnumerable < IGrouping < TKey, TSource > > GroupBy < TSource, TKey > ( this IEnumerable < TSource > source, Func < TSource, TKey > keySelector); c11.indd 315c11.indd 315 2/19/08 5:10:49 PM2/19/08 5:10:49 PM Part I: The C# Language 316 The group r by r.Country into g clause is resolved to GroupBy(r = > r.Country) and returns the group sequence. The group sequence is first ordered by the OrderByDecending() method, then by the ThenBy() method. Next the Where() and Select() methods that you already know are invoked. var countries = Formula1.GetChampions(). GroupBy(r = > r.Country). OrderByDescending(g = > g.Count()). ThenBy(g = > g.Key). Where(g = > g.Count() > = 2). Select(g = > new { Country = g.Key, Count = g.Count() }); Grouping with Nested Objects If the grouped objects should contain nested sequences, you can do that by changing the anonymous type created by the select clause. With this example the returned countries should contain not only the properties for the name of the country and the number of racers, but also a sequence of the names of the racers. This sequence is assigned by using an inner from / in clause assigned to the Racers property. The inner from clause is using the group g to get all racers from the group, order them by the last name, and create a new string based on the first and last name. var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() > = 2 select new { Country = g.Key, Count = g.Count(), Racers = from r1 in g orderby r1.LastName select r1.FirstName + “ “ + r1.LastName }; foreach (var item in countries) { Console.WriteLine(“{0, -10} {1}”, item.Country, item.Count); foreach (var name in item.Racers) { Console.Write(“{0}; “, name); } Console.WriteLine(); } The output now lists all champions from the specified countries: UK 9 Jim Clark; Lewis Hamilton; Mike Hawthorn; Graham Hill; Damon Hill; James Hunt; Nigel Mansell; Jackie Stewart; John Surtees; Brazil 3 Emerson Fittipaldi; Nelson Piquet; Ayrton Senna; Australia 2 c11.indd 316c11.indd 316 2/19/08 5:10:49 PM2/19/08 5:10:49 PM Chapter 11: Language Integrated Query 317 Jack Brabham; Alan Jones; Austria 2 Niki Lauda; Jochen Rindt; Finland 2 Mika Hakkinen; Keke Rosberg; Italy 2 Alberto Ascari; Nino Farina; USA 2 Mario Andretti; Phil Hill; Join You can use the join clause to combine two sources based on specific criteria. But first, let ’ s get two lists that should be joined. With Formula - 1 there ’ s a drivers and a constructors championship. The drivers are returned from the method GetChampions() , and the constructors are returned from the method GetConstructorChampions() . Now it would be interesting to get a list by the year where every year lists the driver and the constructor champion. For doing this, first two queries for the racers and the teams are defined: var racers = from r in Formula1.GetChampions() from y in r.Years where y > 2003 select new { Year = y, Name = r.FirstName + “ “ + r.LastName }; var teams = from t in Formula1.GetContructorChampions() from y in t.Years where y > 2003 select new { Year = y, Name = t.Name }; Using these two queries, a join is done based on the year of the driver champion and the year of the team champion with the clause join t in teams on r.Year equals t.Year . The select clause defines a new anonymous type containing Year , Racer , and Team properties. var racersAndTeams = from r in racers join t in teams on r.Year equals t.Year select new { Year = r.Year, Racer = r.Name, Team = t.Name }; Console.WriteLine(“Year Champion “ + “Constructor Title”); foreach (var item in racersAndTeams) { Console.WriteLine(“{0}: {1,-20} {2}”, item.Year, item.Racer, item.Team); } c11.indd 317c11.indd 317 2/19/08 5:10:49 PM2/19/08 5:10:49 PM Part I: The C# Language 318 Of course you can also combine this to one LINQ query, but that ’ s a matter of taste: int year = 2003; var racersAndTeams = from r in from r1 in Formula1.GetChampions() from yr in r1.Years where yr > year select new { Year = yr, Name = r1.FirstName + “ “ + r1.LastName } join t in from t1 in Formula1.GetContructorChampions() from yt in t1.Years where yt > year select new { Year = yt, Name = t1.Name } on r.Year equals t.Year select new { Year = r.Year, Racer = r.Name, Team = t.Name }; The output displays data from the anonymous type: Year Champion Constructor Title 2004 Michael Schumacher Ferrari 2005 Fernando Alonso Renault 2006 Fernando Alonso Renault 2007 Kimi R ä ikk ö nen Ferrari Set Operations The extension methods Distinct() , Union() , Intersect() , and Except() are set operations. Let ’ s create a sequence of Formula - 1 champions driving a Ferrari and another sequence of Formula - 1 champions driving a McLaren, and then let ’ s find out if any driver has been a champion driving both of these cars. Of course, that ’ s where the Intersect() extension method can help. First get all champions driving a Ferrari. This is just using a simple LINQ query with a compound from to access the property Cars that ’ s returning a sequence of string objects. var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == “Ferrari” orderby r.LastName select r; c11.indd 318c11.indd 318 2/19/08 5:10:50 PM2/19/08 5:10:50 PM Chapter 11: Language Integrated Query 319 Now the same query with a different parameter of the where clause would be needed to get all McLaren racers. It ’ s not a good idea to write the same query another time. You have one option to create a method where you can pass the parameter car : private static IEnumerable < Racer > GetRacersByCar(string car) { return from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r; } However, because the method wouldn ’ t be needed in other places, defining a variable of a delegate type to hold the LINQ query is a good approach. The variable racersByCar needs to be of a delegate type that requires a string parameter and returns IEnumerable < Racer > , similar to the method that was implemented before. For doing this several generic Func < > delegates are defined, so you do not need to declare your own delegate. A Lambda expression is assigned to the variable racersByCar . The left side of the Lambda expression defines a car variable of the type that is the first generic parameter of the Func delegate (a string). The right side defines the LINQ query that uses the parameter with the where clause. Func < string, IEnumerable < Racer > > racersByCar = Car = > from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r; Now you can use the Intersect() extension method to get all racers that won the championship with a Ferrari and a McLaren: Console.WriteLine(“World champion with “ + “Ferrari and McLaren”); foreach (var racer in racersByCar(“Ferrari”). Intersect(racersByCar(“McLaren”))) { Console.WriteLine(racer); } The result is just one racer, Niki Lauda: World champion with Ferrari and McLaren Niki Lauda Partitioning Partitioning operations such as the extension methods Take() and Skip() can be used for easily paging, for example, to display 5 by 5 racers. c11.indd 319c11.indd 319 2/19/08 5:10:50 PM2/19/08 5:10:50 PM [...]... definitions: uint u = 3; byte b = 8; double d = 10.0; uint* pUint= &u; byte* pByte = &b; double* pDouble = &d; // size of a uint is 4 // size of a byte is 1 // size of a double is 8 Next, assume the addresses to which these pointers point are: ❑ pUint: 12 433 32 ❑ pByte: 12 433 28 ❑ pDouble: 12 433 20 Then execute this code: ++pUint; // adds (1*4) = 4 bytes to pUint pByte -= 3; // subtracts (3* 1) = 3 bytes from pByte... adds (1*4) = 4 bytes to pUint pByte -= 3; // subtracts (3* 1) = 3 bytes from pByte double* pDouble2 = pDouble + 4; // pDouble2 = pDouble + 32 bytes (4*8 bytes) The pointers now contain: ❑ pUint: 12 433 36 ❑ pByte: 12 433 25 ❑ pDouble2: 12 433 52 34 4 c12.indd 34 4 2/19/08 5:11 :37 PM Chapter 12: Memory Management and Pointers The general rule is that adding a number X to a pointer to type T with value P gives the... or perform other tasks C# destructors are used far 33 5 c12.indd 33 5 2/19/08 5:11 :34 PM Part I: The C# Language less than their C++ equivalents The problem with C# destructors as compared to their C++ counterparts is that they are nondeterministic When a C++ object is destroyed, its destructor runs immediately However, because of the way the garbage collector works when using C#, there is no way to... contents of memory will look like Figure 12 -3 Note that unlike the stack, memory in the heap is allocated upward, so the free space can be found above the used space 33 2 c12.indd 33 2 2/19/08 5:11 :32 PM Chapter 12: Memory Management and Pointers STACK USED FREE 799996-799999 200 032 arabel 200000-200 031 FREE Stack Pointer HEAP arabel instance 199999 USED Figure 12 -3 The next line of code both declares a... heap that are no longer referenced Immediately after it has done this, the heap will have objects scattered on it, mixed up with memory that has just been freed (see Figure 12-4) 33 3 c12.indd 33 3 2/19/08 5:11 :33 PM Part I: The C# Language In use Free In use In use Free Figure 12-4 If the managed heap stayed like this, allocating space for new objects would be an awkward process, with the runtime having... 0x12F8C7 (12 433 32 to 12 433 35 in decimal) on the stack (there are four locations because an int occupies 4 bytes) Because the stack allocates memory downward, this means that the variables pX will be stored at locations 0x12F8C0 to 0x12F8C3, and pY will end up at locations 0x12F8BC to 0x12F8BF Note that pX and pY also occupy 4 bytes each That is not because an int occupies 4 bytes It is because on a 32 -bit... that 33 9 c12.indd 33 9 2/19/08 5:11 :35 PM Part I: The C# Language data is accessed or manipulated in the most efficient way However, be aware that, more often than not, there are other areas of your code where you can make the necessary performance improvements without resorting to using pointers Try using a code profiler to look for the bottlenecks in your code — one comes with Visual Studio 2008 Low-level... destructor as a safety mechanism in case Dispose() is not called Here is an example of a dual implementation: using System; public class ResourceHolder : IDisposable (continued) 33 7 c12.indd 33 7 2/19/08 5:11 :34 PM Part I: The C# Language (continued) { private bool isDisposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if... Summar y In this chapter, you’ve probably seen the most important enhancements of the 3. 0 version of C# C# is continuously extended With C# 2.0 the major new feature was generics, which provide the foundation for generic type-safe collection classes, as well as generic interfaces and delegates The major feature of C# 3. 0 is LINQ You can use a syntax that is integrated with the language to query any data... past the next free location This is illustrated in Figure 12-1, which shows a stack pointer with a value of 800000 (0xC3500 in hex); the next free location is the address 799999 Location Stack Pointer 800000 USED 799999 FREE 799998 799997 Figure 12-1 33 0 c12.indd 33 0 2/19/08 5:11 :31 PM Chapter 12: Memory Management and Pointers The following code instructs the compiler that you need space in memory . 1 73, Wins: 25 Nelson Piquet, Brazil, Starts: 204, Wins: 23 Ayrton Senna, Brazil, Starts: 161, Wins: 41 c11.indd 31 1c11.indd 31 1 2/19/08 5:10:47 PM2/19/08 5:10:47 PM Part I: The C# Language 31 2 . r.Racer.FirstName + “ “ + r.Racer.LastName); c11.indd 31 3c11.indd 31 3 2/19/08 5:10:48 PM2/19/08 5:10:48 PM Part I: The C# Language 31 4 Resolving the generic SelectMany() method to the types. methods are available with the Enumerable class. c11.indd 32 3c11.indd 32 3 2/19/08 5:10:51 PM2/19/08 5:10:51 PM Part I: The C# Language 32 4 Have you ever needed a range of numbers filled? Nothing