int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = 0;
Array.ForEach(list, delegate(int i) { sum += i; });
System.Console.WriteLine(sum);
First, note that you can make the array initialization smaller. More importantly, note that the call to Array.ForEach doesn't require the generic argument. Instead, this version lets the compiler do the inferencing to pick the right signature. Not quite as compact as Ruby, but still not bad IMO.
First, he asks about debuggability of C# iterators and anonymous methods. That one is easy. It just works. You can step into a foreach loop and wind up in the exact frame for your iterator. Similarly, you can step into a call to an anonymous method and you wind up in the exact right frame. In both cases, the local variables just show up like you'd expect them to, as locals.
By way of example, given the following C# program:
class Program {
static Action<int> GetTest() {
int x = 1;
int y = 1;
return delegate(int i) {
x += i;
y *= y;
Console.WriteLine("{0} {1}", x, y);
};
}
static void Main(string[] args) {
int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Array.ForEach(list, GetTest());
}
}
if I set a breakpoint at the Console.WriteLine statement, I'll see the specific x and y variables used by this activation. If I have concurrent or reentrant activations, the debugger is smart enough to show me the right one.
Cedric's second (and more interesting) point is how useful is this feature at all?
I've been programming in C# 2.0 for over a year now. I regularly find myself using the following two pre-defined delegates from mscorlib in my programs:
namespace System {
public delegate bool Predicate<T>(T item);
public delegate void Action<T>(T item);
}
For example, earlier this week I needed to write some glue code to wire up some unit tests to our build process. The test harness I wrote takes a list of types plus a predicate and runs the tests against all types that match the predicate. Here's the routine I wrote to expose my harness:
public static void RunTests(IEnumerable<Type> types, Predicate<Type> match) {
foreach (Type type in types)
if (match(type))
RunTest(type); // run the actual code I care about
}
Why did I do it this way? Easy. In general, I found that I was typically getting the list of types by calling Assembly.GetTypes() to get all of the types in an assembly. However, I found that there were numerous ways I wanted to filter out which types should actually get used.
Rather than force my harness implementation to hard-code those scenarios into different overloads, it was considerably easier to punt the selection process to the caller. For example, if I want to run my tests against only types with attribute Foo applied, I can do this:
Harness.RunTests(
Assembly.GetCallingAssembly().GetTypes(),
delegate (Type t) { return t.IsDefined(typeof(FooAttribute), false); }
);
To run the same program testing only types that are interfaces, I can do this:
Harness.RunTests(
Assembly.GetCallingAssembly().GetTypes(),
delegate (Type t) { return t.IsInterface; }
);
and thanks to the modern miracle that is first-order predicate logic, I can write the following:
Harness.RunTests(
Assembly.GetCallingAssembly().GetTypes(),
delegate (Type t) { return t.IsEnum && (t.IsDefined(typeof(FlagsAttribute), false) || !t.Namespace.StartsWith("Microsoft.")); }
);
When I wrote Harness.RunTests, I had no way of anticipating that someone would want to test only enums that are either attributed with the [Flags] attribute or are outside of the Microsoft.* namespace. However, because I designed my routine around function-based extensibility, it was pretty trivial to get that functionality out of my otherwise dead-simple code.
OO purists in the crowd are likely noticing that I could have done the same thing using an interface. However, the utility of just writing the code you want without introducing a new nominal type shouldn't be underestimated. Defining a new nominal type introduces all sorts of additional mental gymnastics. Do I make it public or internal? Do I use constructors or factory methods? Does it match the naming conventions of my team? Should it be a struct or class? Should it be sealed?
In the case above, the one-off predicate code I wrote is just that. One-off.
I wrote the code I wanted to write and it just worked.
To me, that's priceless.
Posted
Apr 23 2005, 01:32 PM
by
don-box