I was involved in an interesting discussion about how the C# language has evolved, and how it’s getting harder and harder to teach developers to be proficient in the language with each passing version.
I think that’s our fault.
As leaders and experienced C# developers, we too often teach and explain from our perspective: We learned C# 1.0, learned the new features in 2.0. Then, we learned LINQ and the other syntax additions in 3.0. Now, we’re looking at adding the new 4.0 features to our repertoire of techniques. It makes sense to us, because that’s how we learned.
But we are not them.
That’s an inefficient way for someone approaching C# now to learn the language. Instead, we should explain these new concepts based on how one should learn now, not how we learned. I wish I could take credit for this amazing insight, but I must credit Bjarne Stroustrup. He wrote an essay for C++ Users Journal back in 1999 asserting that the C++ community must change how they taught developers C++. Back then, everyone teaching C++ just assumed that every novice C++ developer was a veteran C developer. And that’s how they taught. Stroustrup instead claimed that developers should learn the best C++ idioms, rather than be taught C, then how to move to C++.
Lambda Expressions as Example
Explaining Lambda expressions is a great example of what I mean in C# today.
When I explain lambda expressions to a novice C# developer, I use this example:
1: var someNumbers = new List<int>(Enumerable.Range(-20, 200));
2: someNumbers.RemoveAll(n => n > 30);
3:
4: bool assertion = someNumbers.TrueForAll(n => n < 40);
Line 1 creates a collection with a bunch of numbers.
Line 2 introduces a lambda. It removes all numbers greater than 30 from the collection. The lambda expression “n => n > 30” describes the condition for all elements to be removed from the list. A lambda expression is a short hand description of a method: n is the parameter. The expression returns a boolean, the result of the test ‘n > 30’.
At this point, I’ll often get a few questions:
‘what type is n"?’
That’s easy: the compiler infers types for the parameters to lambda expressions. Here, the compiler infers that n must be integer, because the collection someNumbers contains integers.
‘there’s no return value’
Yes, I know that’s not a question. But it does express the confusion, and that’s how it’s often stated. I say that the compiler infers the return type. List.RemoveAll() takes a Predicate<T> as its parameter, and Predicate<T> returns a bool. therefore, the expression ‘n > 30’ must be a boolean expression. To illustrate this, I’ll modify the code to show how the compiler checks the type. The lambda expression ‘n => n.ToString()’ doesn’t compile, because n.ToString() evaluates to a string, not a boolean.
Then, I go on to the concept: a lambda expression is a mechanism that enables you to pass code (in the form of a lambda) to another method for execution later. Internally, List.RemoveAll() evaluates the lambda expression parameter for every element in the list. All elements that return true are removed.
I follow the same process to explain the TrueForAll() method call.
Yeah, but what about delegates, anonymous delegates and expression trees?
Those of you that are experienced C# developers are probably concerned: I didn’t mention that lambdas are implemented like anonymous delegates. I didn’t drill down further to state that anonymous delegates are just like delegates.
In short, I didn’t work back from a lambda expression to the equivalent C# 1.0 syntax. But, that’s the point. Unless you have been working with C# since the beginning, those concepts don’t help you. Someone starting to learn C# now doesn’t have that prior knowledge about C# 1.0, or C# 2.0.
Read my explanations above again. They don’t dive into great detail about how a lambda expression is interpreted by the compiler. Rather, they give you a good description of how you would write code using lambda expressions, and what they do for you.
Nothing I said is wrong, but a lot of details are left out. That’s ok, hiding details helps novices get started. We can fill in those holes as these new C# developers develop a greater understanding, and want a deeper understanding.
At that point, they are ready for a different explanation.
Until then, we are better off explaining concepts using explanations that help them, even if those explanations are different than how we learned.