Thursday, August 20, 2009
Inline execution of a lambda expression or anonymous delegate
The way I built my expression builder, essentially you pass in a valid VB.NET expression and the expression builder sticks it onto the end of a Return statement inside a static method of a new class, which it then compiles with the stock VB.NET code compiler. Then I use reflection to dig out the compiled method and returns a Func<bool> that invokes the method, returning the result.
This works great for its intended purpose, but it unfortunately restricts my expressions to being, well, expressions. Boolean expressions. Which means they can't do a whole lot of processing, or whatever. That's okay - I don't really *need* all that much. But I enjoy a challenge, and I thought to myself 'Self, why don't we try to use an inline lambda!'
Great idea - except you can't INVOKE a lambda inline. Example:
bool x = ( () => true ).Invoke();
Doesn't work. Operator '.' cannot be applied to operand of type 'lambda expression'. 'Fine,' says I, 'I'll use an anonymous delegate!'
bool x = ( delegate { return true; } ).Invoke();
Operator '.' cannot be applied to operand of type 'anonymous delegate'. Argh. So as far as i'm aware, this is impossible, probably by design. I'll have to ask Eric Lippert about it, since he's been doing his C# design rationale posts lately.
Thursday, January 15, 2009
Little accomplishments
Case in point - did you know that assignment operations return the result of the assignment? I didn't until recently - it's one of those obscure language bits that almost never come up, but when they do they can really make life easier.
I'm working on my workflow idea (see my previous post for details) - in particular, I'm toying with the idea of composing workflows from sub-workflows - and so I wrote a workflow that takes two workflows and interleaves their workitems together. At first I used a 'Zip' method which does the job, but if there are more workitems in one workflow than the other, the extras are truncated - this is not what I wanted; I want the extra workitems returned correctly.
So I wrote this Workflow() method, which does the job:
do {
if (enum1.MoveNext()) {
yield return enum1.Current;
} else {
enum1Done = true;
}
if (enum2.MoveNext()) {
yield return enum2.Current;
} else {
enum2Done = true;
}
} while (!(enum1Done && enum2Done));
It works, but is verbose and ugly. It is very much imperative code - the *how* of the code obscures much of the *what* and the *why*.
Obscure language features come to the rescue, however, when a little light-bulb goes off in my head. Because assignment operators return the result of the assignment, I can combine the assignment and the test in one statement. The original code becomes this:
var enum1Alive = false;
var enum2Alive = false;
while ((enum1Alive = enum1.MoveNext()) | (enum2Alive = enum2.MoveNext())) {
if (enum1Alive) yield return enum1.Current;
if (enum2Alive) yield return enum2.Current;
}
This may seem like child's play to some of you, and you're probably right, but for me, this represents an accomplishment (albeit small) in my understanding of the language I use.
Wednesday, January 7, 2009
Yield, and the C# state machine
Yield is used to create implementations of the Enumerable pattern - a software pattern that allows you to treat a collection of things as an enumeration, over which you can perform some process. In C#, you consume an enumeration via the 'foreach' statement, like so:
1 IEnumerable<string> ies = new List<string>() { "asd", "ert", "qwe", "fgh" };
2
3 foreach (var s in ies) {
4 Console.WriteLine(s);
5 }
Before C# 2.0, creating a custom enumerable meant implementing the Enumerable Pattern via IEnumerator and IEnumerable. I don't feel like going through this, so here's a short and sweet example I found online.
This is a fairly common implementation. In fact, it is so common that when the C# language designers were conceiving 2.0 of their product, they chose to make it a first-class compiler-driven feature. Enter the yield keyword, which is capable of turning the example above into the following code:
1 public IEnumerable<char> MyChars() {
2 yield return 'A';
3 yield return 'B';
4 yield return 'C';
5 yield return 'D';
6 }
This is much more straightforward, but there is some magic happening here that allows this to happen. First of all, the Enumerable pattern includes the requirement that the processing of the enumeration be lazy - that is, evaluated on a need basis. This allows an enumeration to contain an effectively infinite amount of items. Such an enumeration can be created using this code:
1 public IEnumerable<bool> infiniteAlternatingBools() {
2 bool cur = false;
3 while (true) {
4 yield return cur;
5 cur = !cur;
6 }
7 }
This code generates a list of alternating booleans - True / False / True / False - forever. Surely, this code will result in a locked-up process. Not so, fortunately for us, because behind the scenes (in the magic part) this code is expanded into a proper implementation of the Enumerable pattern - lazy evaluation included. Only when you request the next value is it generated and then provided, meaning this infinite generation can be short-circuited at any moment.
How? Magic, like I said - although this magic can be explained through gratuitous use of .NET Reflector. According to .NET Reflector, my infiniteAlternatingBools() method looks like this:
1 public IEnumerable<bool> infiniteAlternatingBools()
2 {
3 <infiniteAlternatingBools>d__5 d__ = new <infiniteAlternatingBools>d__5(-2);
4 d__.<>4__this = this;
5 return d__;
6 }
What? This is a mess. What is reflector telling me about this code?
In a nutshell, Reflector is saying that the C# compiler has, behind my back, taken the code I wrote and moved it into an anonymous private class. The constructor of that class takes an integer in its constructor, which the rewritten method initializes to -2. It also sets a 'this' property to the class that contains infiniteAlternatingBools - probably allowing it to access the original class's private members. Then the rewritten method returns the instance of that anonymous class - which suggests that it implements IEnumerable<bool>.
Kind of rude, don't you think? Replacing our carefully written infinite loop with some object creation? Actually the C# compiler has done us a favor - if you look in the anonymous class it generated you'll find the original code you wrote, albeit in a form you might not fully recognize. Here's the listing of the class (cleaned up a little bit from the Reflector version, which contains illegal characters):
1 [CompilerGenerated]
2 private sealed class d__5 :
3 IEnumerable<bool>, IEnumerable,
4 IEnumerator<bool>, IEnumerator,
5 IDisposable {
6
7 // Fields
8 private int state;
9 private bool current;
10 public Program.anon _this;
11 private int initialThreadId;
12 public bool _6;
13
14 // Methods
15 [DebuggerHidden]
16 public d__5(int state) {
17 this.state = state;
18 this.initialThreadId = Thread.CurrentThread.ManagedThreadId;
19 }
20
21 public bool MoveNext() {
22 switch (this.state) {
23 case 0:
24 this.state = -1;
25 this._6 = false;
26 break;
27
28 case 1:
29 this.state = -1;
30 this._6 = !this._6;
31 break;
32
33 default:
34 return false;
35 }
36 this.current = this._6;
37 this.state = 1;
38 return true;
39 }
40
41 [DebuggerHidden]
42 IEnumerator<bool> IEnumerable<bool>.GetEnumerator() {
43 if ((Thread.CurrentThread.ManagedThreadId == this.initialThreadId) && (this.state == -2)) {
44 this.state = 0;
45 return this;
46 }
47 Program.anon.d__5 d__ = new Program.anon.d__5(0);
48 d__._this = this._this;
49 return d__;
50 }
51
52 [DebuggerHidden]
53 IEnumerator IEnumerable.GetEnumerator() {
54 return this;
55 }
56
57 [DebuggerHidden]
58 void IEnumerator.Reset() {
59 throw new NotSupportedException();
60 }
61
62 void IDisposable.Dispose() {
63 }
64
65 // Properties
66 bool IEnumerator<bool>.Current {
67 [DebuggerHidden]
68 get {
69 return this.current;
70 }
71 }
72
73 object IEnumerator.Current {
74 [DebuggerHidden]
75 get {
76 return this.current;
77 }
78 }
79 }
This listing is a bit hard to understand. There are fields called state, current, _this, initialThreadID, and _6. There are the IEnumerable and IEnumerator implementations - MoveNext, Current, GetEnumerator, and Reset. There's a constructor (taking an int). What can all this mean? More importantly, where's my infinite loop?
There is no infinite loop. My code is still here, but it's been turned into a state machine. The value that gets passed in to the constructor (-2) tells this state machine that it's in the initial state. When GetEnumerator is called, it checks to see if it's in its initial state - if it's not, it creates a new version of itself and returns that - but if it is, then it moves into state '0' and returns itself. When MoveNext is called, it uses the state to determine what value to set as the 'current' property. At state 0, the initial value is returned - which the C# compiler correctly determined to be false, given my initial 'bool cur = false;' statement. It also moves into state 1. Subsequent calls to MoveNext will call my code which alternates this _6 value, which is a boolean, between true and false - mimicking the behavior I coded.
My infinite loop turned into a lazily evaluated Enumerable Pattern implementation which uses a state machine to decide on what the 'current' value should be whenever MoveNext is called. pretty damned cool if you ask me.
The coolest thing about this is that you can use any C# constructs you want in your enumerable, and create some incredibly complex generators. In my case, I'm taking advantage of the built-in state machine to create a workflow-like process. One place I plan on using this is in EverHarvest 2. Here's a simplified version of what my workflow might look like once fully implemented:
1 public IEnumerable<WorkUnit> Workflow() {
2 yield return new Initialize();
3
4 while (true) {
5 var wp = GetNextWaypoint();
6 var wtw = new WalkingToWaypoint(wp);
7 while (!wtw.ReachedWaypoint) {
8 yield return wtw;
9
10 var tgt = new Targetting();
11 yield return tgt;
12
13 if (tgt.FoundTarget) {
14 if (IsNode(tgt.TargetName)) {
15 var wtn = new WalkingToHarvestable(tgt.TargetName, tgt.TargetLocation);
16 while (!wtn.ReachedNode) {
17 yield return wtn;
18 }
19
20 var h = new Harvesting();
21 while (!h.DoneHarvesting) {
22 yield return h;
23 }
24 }
25 }
26 }
27 }
28 }
Notice how simple this is to understand. It reads very procedurally, and yet because this is lazily evaluated, this process can be interrupted at any of the yield points. This control lies with the code that is enumerating through the workflow - that code can act as the gatekeeper, deciding when to get the next work item, when to execute it, when to break out of the loop, what data each bit should have, etc. This can be done in a foreach statement, or I can use the older MoveNext / Current members.
I hope this helps those of you who are still reading to understand how the yield statement can be used to take advantage of the state machine functionality that the C# compiler provides for us. In terms of readability and maintenance, it has proven to be a real boon for me. I hope this has helped you to find the same benefit.
Edit: Here's the disassembled MoveNext() method from Reflector - I haven't cleaned it up a bit. Lots of red squiglies in this one...
1 private bool MoveNext()
2 {
3 bool CS$4$0002;
4 switch (this.<>1__state)
5 {
6 case 0:
7 this.<>1__state = -1;
8 this.<>2__current = new Initialize();
9 this.<>1__state = 1;
10 return true;
11
12 case 1:
13 this.<>1__state = -1;
14 goto Label_01CB;
15
16 case 2:
17 goto Label_00B4;
18
19 case 3:
20 goto Label_00E0;
21
22 case 4:
23 goto Label_0159;
24
25 case 5:
26 goto Label_0198;
27
28 default:
29 return false;
30 }
31 Label_01CB:
32 CS$4$0002 = true;
33 this.<wp>5__1 = this.<>4__this.GetNextWaypoint();
34 this.<wtw>5__2 = new WalkingToWaypoint(this.<wp>5__1);
35 while (!this.<wtw>5__2.ReachedWaypoint)
36 {
37 this.<>2__current = this.<wtw>5__2;
38 this.<>1__state = 2;
39 return true;
40 Label_00B4:
41 this.<>1__state = -1;
42 this.<tgt>5__3 = new Targetting();
43 this.<>2__current = this.<tgt>5__3;
44 this.<>1__state = 3;
45 return true;
46 Label_00E0:
47 this.<>1__state = -1;
48 if (this.<tgt>5__3.FoundTarget && this.<>4__this.IsNode(this.<tgt>5__3.TargetName))
49 {
50 this.<wtn>5__4 = new WalkingToHarvestable(this.<tgt>5__3.TargetName, this.<tgt>5__3.TargetLocation);
51 while (!this.<wtn>5__4.ReachedNode)
52 {
53 this.<>2__current = this.<wtn>5__4;
54 this.<>1__state = 4;
55 return true;
56 Label_0159:
57 this.<>1__state = -1;
58 }
59 this.<h>5__5 = new Harvesting();
60 while (!this.<h>5__5.DoneHarvesting)
61 {
62 this.<>2__current = this.<h>5__5;
63 this.<>1__state = 5;
64 return true;
65 Label_0198:
66 this.<>1__state = -1;
67 }
68 }
69 }
70 goto Label_01CB;
71 }
Wednesday, December 10, 2008
Tuples...
You may not realize it yet, but tuples have been around in .NET since 2.0 - the KeyValuePair generic class is, essentially, an immutable tuple of any two values. Using it as such, though, is unwieldy in my opinion - its purpose was to facilitate enumerating through dictionaries, and while it works fine in this context, KeyValuePair is ill-suited to more generic purposes.
During my experimentation with F# and other functional languages, I've come to see the Tuple type as a very valuable data structure that I miss very much when working on "real-world" code (real-world in this context meaning what I do for a living). .NET has no Tuple type, and while I'm aware that .NET 4 will have this type, that doesn't help me now. I could use the F# tuple types in my C# code, yes - but I'd have to distribute the F# binaries with my software, and I don't want to do that if I'm not using F#. My only other options are to use someone else's library, or roll my own. I've opted for the latter.
I've implemented four immutable tuple struct types, which hold between two and 5 values. Additionally, I implemented several helper methods and extension methods that make working with the Tuples a little easier in C#. Here's the code for the two-value Tuple struct:
1 public struct Tuple<T1, T2> {
2 private readonly T1 _value1; public T1 Value1 { get { return _value1; } }
3 private readonly T2 _value2; public T2 Value2 { get { return _value2; } }
4 public Tuple(T1 value1, T2 value2) { _value1 = value1; _value2 = value2; }
5
6 public override bool Equals(object obj) {
7 if (!(obj is Tuple<T1, T2>)) return false;
8 if (obj == null) return false;
9
10 Tuple<T1, T2> t = (Tuple<T1, T2>)obj;
11 return (Value1.Equals(t.Value1) && Value2.Equals(t.Value2));
12 }
13
14 public override int GetHashCode() {
15 return Value1.GetHashCode() ^ Value2.GetHashCode();
16 }
17
18 public KeyValuePair<T1, T2> AsKeyValuePair() {
19 return new KeyValuePair<T1, T2>(Value1, Value2);
20 }
21 }
22 ...
23
24 public static class Tuples {
25 public static Tuple<T1, T2> Tuple<T1, T2>(T1 value1, T2 value2) {
26 return new Tuple<T1, T2>(value1, value2);
27 }
28 ...
29
30 public static Tuple<T1, T2> Default<T1, T2>() {
31 return new Tuple<T1, T2>(default(T1), default(T2));
32 }
33 ...
34
35 public static IEnumerable<Tuple<T1, T2>> Zip<T1, T2>(IEnumerable<T1> first, IEnumerable<T2> second) {
36 var enum1 = first.GetEnumerator();
37 var enum2 = second.GetEnumerator();
38
39 while (enum1.MoveNext() && enum2.MoveNext()) {
40 yield return Tuple(enum1.Current, enum2.Current);
41 }
42 }
43 ...
44 }
45
46 public static class TupleExtensions {
47 public static Tuple<IEnumerable<T1>, IEnumerable<T2>> Unzip<T1, T2>(this IEnumerable<Tuple<T1, T2>> ienum) {
48 var first = new List<T1>();
49 var second = new List<T2>();
50
51 foreach (var t in ienum) {
52 first.Add(t.Value1);
53 second.Add(t.Value2);
54 }
55
56 return Tuples.Tuple(first.AsEnumerable(), second.AsEnumerable());
57 }
58 ...
59
60 public static Tuple<T1, T2> AsTuple<T1, T2>(this KeyValuePair<T1, T2> kvp) {
61 return Tuples.Tuple(kvp.Key, kvp.Value);
62 }
63 }
The '...'s denote where the pattern is extended to cover all the tuple value counts from 2 to 5.
Only a little explanation is really needed here - the Tuple structs are read-only, so once they're initialized they can't be reset. In my experience this isn't a problem - I've never really *needed* that functionality where I can't just create a new Tuple. The static 'Tuples' class makes it easier to initialize a Tuple - using this, I can create a Tuple from existing data without having to add the type parameters. The compiler figures it out from the existing type data. Can't do this with constructors, sadly. The 'AsKeyValuePair' and 'AsTuple' methods (which only work with the 2-tuple struct) are pretty self explanatory.
The Zip method takes two or more IEnumerables and 'zips' them together into a single IEnumerable of Tuples. The Unzip method sort performs the reverse, although since you can only return one value from a method I package the unzipped Enumerables in a single Tuple.
If you'd like to use this in your own projects, I've uploaded my Tuples file to PasteBin - you can get to it from here. No attribution needed - though it would be nice if you'd drop a line here to let me know it's been useful to you. =)
Tuesday, October 21, 2008
Configuration Section Handlers via IConfigurationSectionHandler redux
Anyway - IConfigurationSectionHandler is an interface in the System.Configuration namespace that, when implemented, allows your software to define and read custom configuration sections. The idea is that you'll put these configuration sections in your app.config or web.config file and permit configuration that doesn't require recompiling your codebase after making changes.
There is another way to handle custom configuration sections - you could inherit from System.Configuration.ConfigurationSection and provide attributes on your properties that you want to be read from configuration. This approach certainly works - there's a writeup on this approach on MSDN here - but I prefer IConfigurationSectionHandler because it affords me more control over the process, even though apparently its use has been deprecated. =(
The first step before you start touching code is to determine the shape of your XML configuration section. I'll use planets as an (admittedly contrived) example:
1 <planets>
2 <planet name="Mercury" distanceFromSun=".38"
3 diameter="4880" mass="3.30e23" />
4 <planet name="Venus" distanceFromSun=".72"
5 diameter="12103" mass="4.869e24" />
6 <planet name="Earth" distanceFromSun="1"
7 diameter="12756" mass="5.972e24" />
8 <planet name="Mars" distanceFromSun="1.52"
9 diameter="6794" mass="6.4219e23" />
10 <planet name="Jupiter" distanceFromSun="5.2"
11 diameter="142984" mass="1.900e27" />
12 <planet name="Saturn" distanceFromSun="9.54"
13 diameter="120536" mass="5.68e26" />
14 <planet name="Uranis" distanceFromSun="19.218"
15 diameter="51118" mass="8.683e25" />
16 <planet name="Neptune" distanceFromSun="30.06"
17 diameter="49532" mass="1.0247e26" />
18 <planet name="Pluto(?)" distanceFromSun="39.5"
19 diameter="2274" mass="1.27e22" />
20 </planets>
This is a pretty straightforward bit of XML. A single root 'planets' element that surrounds nine 'planet' elements, each with some attributes - name, distance from the sun (in astronomical units), diameter in kilometers, and mass in kilograms. The next step is deciding how to represent this in your program. Certainly, you could leave it as XML - there's nothing wrong with that - but in practice I find it more likely you'll want to use an object and its properties to hold and manipulate this data. Here's an example:
1 public class Planet {
2 public string Name { get; set; }
3 public float DistanceFromSun { get; set; }
4 public int Diameter { get; set; }
5 public float Mass { get; set; }
6
7 public Planet(string name, float distanceFromSun,
8 int diameter, float mass) {
9 Name = name;
10 DistanceFromSun = distanceFromSun;
11 Diameter = diameter;
12 Mass = mass;
13 }
14 }
This is pretty straightforward so far. All we need now is a way to go from the XML representation to a collection of Planet instances. That's what IConfigurationSectionHandler is for.
In order to do this, your project will need a reference to System.Configuration, if it doesn't have one already. For our Planets configuration, we'll create a new handler called PlanetsConfigurationHandler and with it implement IConfigurationSectionHandler.
IConfigurationSectionHandler is a very small interface. It contains only one method to implement:
object Create(object parent, object configContext, XmlNode section);
As you can see this method is passed a parent object, a context object, and an XmlNode which represents the section itself. Now, I'm going to be completely honest and say that I have no idea what parent and configContext are for. The section parameter is the only one I'm interested in, and it's the only one I've ever used, so I'm going to say it's pretty safe to ignore them for now.
A word of caution. The Configuration system that is responsible for calling your handler will assume that you're not storing any state in your handler, and that it is thread-safe. This means that you really shouldn't use any external or internal state in the body of the Create method. You should assume that your handler will be called multiple times per instance, in random order.
So by this point, all you need to do is write your XML parsing code. For the sake of sanity and simplicity, I'm going to convert this old 1.0-style XmlNode object into a 3.5-style XDocument object, and work with it from there. I prefer to keep up with the current developments in programming languages. If you can't use 3.5 for whatever reason... well, there are plenty of System.Xml references available out there. You're resourceful, you'll find something. ;)
Here's my handler:
1 public class PlanetsConfigurationHandler
2 : IConfigurationSectionHandler {
3
4 public object Create(
5 object parent, object configContext, XmlNode section) {
6
7 XDocument doc = XDocument.Parse(section.OuterXml);
8 XElement root = (XElement)doc.FirstNode;
9
10 IList<Planet> rList = new List<Planet>();
11
12 foreach (var element in root.Elements() ) {
13 if (element.Name != "planet")
14 throw new ConfigurationErrorsException(
15 "planets section only accepts" +
16 " 'planet' elements.");
17
18 try {
19 string name = element.Attribute("name").Value;
20 float distanceFromSun =
21 float.Parse(
22 element.Attribute("distanceFromSun").Value);
23 int diameter =
24 int.Parse(
25 element.Attribute("diameter").Value);
26 float mass =
27 float.Parse(
28 element.Attribute("mass").Value);
29
30 Planet newPlanet =
31 new Planet(name, distanceFromSun, diameter, mass);
32
33 rList.Add(newPlanet);
34 } catch (Exception ex) {
35 throw new ConfigurationErrorsException(
36 "Error reading planet element."
37 , ex);
38 }
39 }
40
41 return rList;
42 }
43 }
Please excuse the weird formatting - I don't want the code lines to wrap.
So - pretty straightforward. I take the XML section data and using the XDocument elements I parse it for its content. A couple things to notice here: I'm assuming that the root element is correctly named 'planets' - in fact, it's possible it'll be named something else - you'll see how later - so I'm trusting here that the configuration system has passed me the correct section. We'll go with that for now. Second, I am doing some validation on the inner xml - none will be done for me - and I'm making sure that the only thing in my configuration section are 'planet' elements. Third, I've wrapped the rest of the body of the iterator in a try/catch block. I'm using .Parse methods which will throw an exception if the string they're trying to parse is null or malformed - I catch that exception, then throw a ConfigurationErrorsException, passing in the original exception in its constructor to preserve context. This will allow callers of your library to understand what went wrong, when something does.
We've got one last thing to do in order to actually *use* this handler. You've got to register it with the configuration system by adding a bit of XML in the 'Configuration' section:
1 <configSections>
2 <section
3 name="planets"
4 type="ConfigDemo.PlanetsConfigurationHandler, ConfigDemo"/>
5 </configSections>
This should be pretty much self-explanatory. This bit of XML tells the configuration system that you've got a custom handler that will handle any sections named 'planets'. Earlier I mentioned that it's possible to use an arbitrary name when dealing with a configuration section. Well, this is where that name is selected. If you change the 'name' attribute's value here, you'll have to use the same value for the name of the section itself - that is, the root element of the section's XML. You'll also need to use that name when you retrieve the section itself - which brings me to my final bit of code:
1 class Program {
2 static void Main(string[] args) {
3 var planets =
4 (IList<Planet>)
5 ConfigurationManager.GetSection("asdasd");
6
7 string planetTemplate = "{0}: {1}au, {2}km, {3}kg";
8
9 foreach (var planet in planets) {
10 Console.WriteLine(
11 string.Format(planetTemplate,
12 planet.Name,
13 planet.DistanceFromSun,
14 planet.Diameter,
15 planet.Mass)
16 );
17 }
18
19 Console.ReadLine();
20 }
21 }
This is a console application that retrieves the configuration section 'planets' from App.config, then displays the planets on the console. Very simple. Notice that we need to cast the return from ConfigurationManager.GetSection to the type we expect to receive - that method returns 'object', which is to be expected since it couldn't possibly know what type to expect your handler to return. Once you've got your data out of configuration, it's up to you to decide what to do with it.
So, that's that. I do however have one last thing to say. I've been getting into functional programming quite a bit, and as such I like to exercise those newly-forming muscles any chance I can get. As I mentioned above, the Create method needs to avoid internal or external state. Any time it is given an input (the xml configuration section) it should provide the same output. This sounds an awful lot like it needs to be a pure function, to me - so let's change the method to make it a little more obvious that that's what we're going for:
1 public class PlanetsConfigurationHandler
2 : IConfigurationSectionHandler {
3
4 public object Create(
5 object parent, object configContext, XmlNode section) {
6 XDocument doc = XDocument.Parse(section.OuterXml);
7 XElement root = (XElement)doc.FirstNode;
8
9 if (root.Descendants().Any(e => e.Name != "planet"))
10 throw new ConfigurationErrorsException(
11 "planets section only accepts" +
12 " 'planet' elements.");
13
14 try {
15 return
16 (from p in root.Descendants()
17 let newPlanet = new Planet(
18 p.Attribute("name").Value,
19 float.Parse(p.Attribute("distanceFromSun").Value),
20 int.Parse(p.Attribute("diameter").Value),
21 float.Parse(p.Attribute("mass").Value)
22 )
23 select newPlanet).ToList();
24 } catch (Exception ex) {
25 throw new ConfigurationErrorsException(
26 "Error reading planet element."
27 , ex);
28 }
29 }
30 }
Whoa. Way different - and yet the functionality is the same. Much fewer lines of code here, as well. I'll leave it as an exercise to the reader to work this out, if it's not already apparent - it's not a very tough query, really. I have faith. :)
Good luck with your own IConfigurationSectionHandler implementations - feel free to shoot me any questions you may have about the process, and I'll answer as best I can.