|
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Features LINQ Unleashed: Programming with Anonymous Types
Save design time, coding time and debug time
By: Paul Kimmel
Feb. 6, 2009 04:00 PM
Finding a beginning is always a little subjective in computer books. This is because so many things depend on so many other things. Often, the best we can do is put a stake in the ground and start from that point. Anonymous types are our stake. Anonymous types use the keyword var. Var is an interesting choice because it is still used in Pascal and Delphi today, but var in Delphi is like ByRef in Visual Basic (VB) or ref in C#. The var introduced with .NET 3.5 indicates an anonymous type. Now, our VB friends are going to think, "Well, we have had variants for years; big deal." But var is not a dumbing down and clogging up of C#. Anonymous types are something new and necessary. Before looking at anonymous types, let's put a target on our end goal. Our end goal is to master LINQ (integrated queries) in C# for objects, Extensible Markup Language (XML), and data. We want to do this because it's cool, it's fun, and, more important, it is very powerful and expressive. To get there, we have to start somewhere and anonymous types are our starting point.
An important concept here is that you don't write code to define the ad hoc types - C# does - so, you save time by not writing code. You save design time, coding time, and debug time. Microsoft pays that cost. Anonymous types are the vessel that permits you to use these ad hoc types. By the time you are done with this chapter, you will have mastered the left side of the operator and a critical part of LINQ. In addition, to balance the book, the chapters are laced with useful or related concepts that are generally helpful. This chapter includes a discussion on generic anonymous methods. Understanding Anonymous Types Anonymous types support IntelliSense, but the class should not be referred to in code, just the members. The following list includes some basic rules for using anonymous types:
The single greatest value and the necessity of anonymous types is they support creating single-use elements and composite types returned by LINQ queries without the need for the programmer to fully define these types in static code. That is, the designers can focus significantly on primary domain types, and the programmers can still create single-use anonymous types ad hoc, letting the compiler write the class definition. Finally, because anonymous types are immutable - think no property setters - two separately defined anonymous types with the same field values are considered equal. Programming with Anonymous Types Defining Simple Anonymous Types var title = "LINQ Unleashed for C#"; uses the anonymous type syntax and assigns the string value to "LINQ Unleashed for C#". This code is identical in the MSIL to the following: string title = "LINQ Unleashed for C#"; This emitted code equality can be seen by looking at the Intermediate Language (IL) with the Intermediate Language Disassembler (ILDASM) utility (see Figure 1). The support for declaring simple anonymous types exists more for completeness and symmetry than utility. In departmental language wars, purists are likely to rail against such use as it adds ambiguity to code. The truth is the type of the data is obvious in such simple use examples and it hardly matters. Using Array Initializer Syntax The first eight numbers in the Fibonacci system are defined on the line that begins var fibonacci. Fibonacci numbers start with the number 1 and the sequence is resolved by adding the prior two numbers. (For more information on Fibonacci numbers, check out Wikipedia; Wikipedia is wicked cool at providing detailed facts about such esoterica.) Even in the example shown in Listing 1, you are less likely to get involved in language ambiguity wars if you use the actual type int[] instead of the anonymous type syntax for arrays. (Listings 1 through 16 can be downloaded here.) Creating Composite Anonymous Types The anonymous type defined on the line starting with var dena emits a class, referred to as a projection, in the MSIL (see Figure 2). Although the projection's name - the class name - cannot be referred to in code, the member elements - defined by the member declarators First and Last - can be used in code and IntelliSense works for all the elements of the projection (see Figure 3). Another nice feature added to anonymous types is the overloaded ToString method. If you look at the MSIL or the output from Listing 2, you will see that the field names and field values, neatly formatted, are returned from the emitted ToString method. This is useful for debugging. Adding Behaviors to Anonymous Composite Types Adding Methods to Anonymous Types The technique consists of defining an anonymous delegate and assigning that anonymous delegate to the generic Func class. In the example, Concat was defined as an anonymous delegate that accepts two strings, concatenates them, and returns a string. You can assign that delegate to a variable defined as an instance of Func that has the three string parameter types. Finally, you assign the variable Concat to a member declarator in the anonymous type definition (referring to var dena = new {First="Dena", Last="Swanson", Concat=Concat}; now). After the plumbing is in place, you can use IntelliSense to see that the behavior - Concat - is, in fact, part of the anonymous type dena, and you can invoke it in the usual manner. Using Anonymous Type Indexes in for Statements The only requirement that must be met for an object to be the iterand in a foreach statement is that it must functionally represent an object that implements IEnumerable or IEnumerable<T> - the generic equivalent. Incidentally, this is also the same requirement for bindability, as in binding to a GridView. Tip: At any time, you can branch in for or foreach statements with the break or continue keywords or the goto, return, or throw statements. An all-too-common use of the for construct is to copy a subset of elements from one collection of objects to a new collection, for example, copying all the customers in the 48843 ZIP code to a customersToCallOn collection. In C# 2.0, the yield return and yield break key phrases actually played this role. For example, yield return signaled the compiler to emit a state machine in MSIL - in essence, it emitted the copy collection for you. In .NET 3.5, the ability to query collections, datasets, and XML to essentially ask questions about data or copy some elements is one of those things that LINQ does very well. Listing 7 shows code that uses a LINQ statement to return just the numbers in the Fibonacci short sequence that are divisible by 3. (For now, don't worry about understanding all of the elements of the query.) The LINQ query - used as the iterand in the foreach statement - makes up this part of the Listing 7: from f in fibonacci where f % 3 == 0 select f For now, it is enough to know that this query meets the requirement that it returns an enumerable result, in fact, IEnumerable<T> where T is an int type. If this is your first experience with LINQ, the query might look strange. The capability and power and this book will quickly make them familiar and desirable friends. For now, it is enough to know that queries meet the requirement of an enumerable resultset and can be used in a foreach statement. Anonymous Types and Using Statements The help documentation will verify that SqlConnection is derived from DBConnection, which, in turn, implements IDisposable. You can use a tool like Anakrino or Reflector - free decompilers and disassemblers - to see that Dispose in DBConnection invokes the Close method on a connection. To really understand how things are implemented, you can use ILDASM - or one of the previously mentioned decompilers - and look at the MSIL that is emitted. If you look at the code in Listing 8's IL, you can clearly see the substitution of using for a properly configured try...finally block. (The try element - after SqlConnection creation - and the finally block invoking Dispose are shown in bold font in Listing 9.) You don't have to master IL to use .NET effectively, but you can learn from it and writing .NET emitters - code that emits IL directly - is supported in the .NET Framework. As shown in the MSIL, you can infer, for example, that the proper way to use try...finally is to create the protected object, try to use it, and, finally, clean it up. If you read a little further - in the finally block starting with IL 0030 - you can see that the compiler also put a check in to ensure that the protected object, the SqlConnection, is compared with null before Dispose is called. This code is demonstrated in IL 0030, IL 0031, IL 0032, and the branch statement on IL 0036. Returning Anonymous Types from Functions Although it is intellectually satisfying to play with the reflection subsystem, writing code like that in Listing 10 is a slow and painful means to an end. (In addition, the code in Listing 10, as written, is fraught with the potentiality for bugs due to null values being returned from GetType, GetProperty, and GetValue.) Databinding Anonymous Types This section looks at how you can combine cool technologies, such as anonymous types, AJAX, HttpWebRequests, HttpWebResponses, and queries to Yahoo!'s stock-quoting capability, and assemble a web stock ticker. Aside from the code, a demonstration of data-binding anonymous types, and a brief description of what role the various technology elements are playing, this book doesn't elaborate in detail on features like AJAX (because of space and topic constraints). (For more information on web programming, see Stephen Walther's ASP.NET 3.5 Unleashed.) The sample (in Listing 11) is actually very easy to complete, but uses some very cool technology and plumbing underneath. In the solution, a website project was created. The application contains a single .aspx web page. On that page, a ScriptManager, UpdatePanel (both AJAX controls), a DataList, Label, and AJAX Timer are added. The design-time view of the page is shown in Figure 4 and the runtime view is shown in Figure 5. (Listing 12 shows the settings for the Web controls.) Because of anonymous types, the code to actually query the stock process from Yahoo! is very short (see Listing 11). The method InnerGetQuote has a properly formatted uniform resource locator (URL) query for the Yahoo! stock-quoting feature. Next, an HttpWebRequest sends the URL query to Yahoo! Then, the HttpWebResponse - returned by request.GetResponse - is requested and a StreamReader reads the response. Easy, right? All of this code is run by the Update method. Update creates anonymous types containing a Stock and Quote field (which are populated by the GetQuote and InnerGetQuote methods). An anonymous array of these quote objects is created and all of this is bound to the DataList. The DataList itself has template controls that are data bound to the Stock and Quote fields of the anonymous type. Figure 6 shows the template design of the DataList. The very easy binding statement is shown in Figure 7. All of the special features, such as template editing and managing bindings, are accessible through the DataList Tasks button, which is shown to the right of the DataList in Figure 4. You can also edit elements such as binding statements directly in the ASP designer. Listing 12 shows the ASP/HTML for the web page. The really neat thing about this application (besides getting stock quotes) is that the postbacks happen transparently with AJAX. The way AJAX works is that an asynchronous postback happens and all of the code runs except the part that renders the new page data. Instead, text is sent back and JavaScript updates small areas of the page. The underlying technology for AJAX is an XHTMLRequest, and this technology in its raw form has been around for a while. But, the raw form required wiring up callbacks and spinning your own JavaScript. You can still handcraft AJAX code of course, but now there are web controls, such as the UpdatePanel and Timer, that take care of the AJAX plumbing for you. The elements that initiate the AJAX behavior are called triggers. Triggers can really be any postback event. Listing 12 uses the AJAX Timer's Tick event. (And, if you want this to actually look like a ticker, play with some styles and add some color.) Testing Anonymous Type Equality Note: It is possible to use reflection to get type information about anonymous types, and you might want to do this, occasionally, for anonymous types returned from methods. However, the actual name of the anonymous type can vary between compilations, so devising a way to use the class name probably has no reliable uses. If you want to test member equality, use the Equals method (defined by all objects). Anonymous types with the same order, type, and name, type, and value of member declarators also produce the same hash; the hash is the basis for the equality test. Listing 13 provides some samples of anonymous types followed by equality tests and comments indicating those that produce the same anonymous types and those that have memberwise equality. The anonymous types audioBook and songBook1 produce the same anonymous type. These are the only two that produce the same hash and, as a result, the Equals method returns true. The other anonymous types are similar, but either the member declarators are different - songBook1 uses the member declarator Artist and songBook2 uses Singer - or the order of the declarators are different - referring to audioBook and audioBook1. Using Anonymous Types with LINQ Queries This whole book is about LINQ, so Listing 14 shows a couple of LINQ examples without getting too far ahead in upcoming chapter material. Again, each example also has a brief description. The first query - from n in numbers orderby n descending select n-sorts the integers 1 to 7 in reverse order and stuffs the results in the anonymous type all. The second query - from song in songs select new {Title=song} - shapes the array of strings in songs to an enumerable collection of anonymous objects with a property Title. (The second example takes an array of strings and shapes it into an array of objects with a well-named property.) Introducing Generic Anonymous Methods Anonymous methods behave like regular methods except that they are unnamed. They were introduced as an alternative to defining delegates that did very simple tasks, where full-blown methods amounted to more than just extra typing. Anonymous methods also evolved further into Lambda Expressions, which are even shorter (terse) methods. For now, this section takes an introductory look at anonymous generic methods. An anonymous method is like a regular method but uses the delegate keyword, and doesn't require a name, parameters, or return type. Listing 15 shows a regular method (used as a delegate for the CancelKeyPress event, Ctrl+C in a console application) and an anonymous delegate that performs the same role. Tip: To quickly stub out an event-handling method, type the object.eventname, the += operator, and press the Tab key twice. The regular method (used as a delegate) is named ConsoleCancelEventHandler. Although the double-Tab trick generates these stubbed delegates for you, they are overkill for one-line event handlers. The second statement that begins with the Console.CancelKeyPress += delegate demonstrates an anonymous method (delegate) that is equivalent to the longer form of the method. Notice that because the parameters in the delegate aren't used, they are omitted from the anonymous delegate. You have the option of using the parameter types and names if they are needed in the delegate. Using Anonymous Generic Methods For all intents and purposes, Factorial is a nested function. Listing 16 used Func<long, long>, where the first long parameter represents the return type and the second is the parameter. Notice that the listing also used a named parameter for the anonymous delegate. Implementing Nested Recursion There is a class called StackFrame. StackFrame permits getting methods (and information from the call stack) and you can use this class and reflection to invoke the anonymous delegate recursively. (This code is obviously esoteric - referred to this as programmer esoterrorism - but it is fun and demonstrates a lot of features of the framework in a little bit of space, as shown in Listing 17.) Again, writing code like the Factorial delegate in Listing 17 is only fun for the writer, but elements of it do have utility. For example, anonymous delegates like the Factorial can be useful for one-time, simple event handling. Assigning behaviors to the Func<T> delegate type effectively makes nested functions and reusable delegates that can be passed as arguments, a very dynamic way to program. Getting the StackFrame can be a great way to create a utility that tracks function calls during debugging - like writing the StackTrace to the Debug window in a way that is useful to you - and reflection has many uses. Reflection can be useful for dynamically loaded assemblies, as demonstrated by NUnit and Visual Studio's unit testing. Summary As you see anonymous types used throughout the book for query results, remember anonymous types are immutable, the same type is code generated if the member declaratory - field name - type, number, and order are identical. • • • This chapter is an excerpt from the book, LINQ Unleashed authored by Paul Kimmel, published by Sams Publishing, July 2008. Reader Feedback: Page 1 of 1
SOA World Latest Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||