(You are currently looking at the first edition version of this page. This page is also available for the second and third editions.)

Notes for Chapter 1: The changing face of C# development

1.1.1: Productizing Product

The Product class in listing 1.1 leaves a few things to be desired. These are partly stylistic, but still far from arbitrary.

All of these points are ones to think about for real code, which has different motivations than sample code in books. In this case, I didn't want to add modifiers like private and sealed, in order to keep the code as simple as possible.

1.1.2: Implicit casting in foreach loops over generic collections

After changing the returned list of products from an ArrayList to a List<Product>, the book makes the following bold claim:

Similarly, the invisible cast in the foreach loop has gone. It’s hard to tell the difference, given that it’s invisible, but it really is gone. Honest. I wouldn’t lie to you. At least, not in chapter 1...

Well, it's sort of true and it sort of isn't. The compiler effectively converts the foreach loop into code which does contain a cast - but then it notices that the cast is just an identity conversion, so it optimizes it away from the compiled code.

In other cases, there will still be a cast in the compiled code. Consider this perfectly valid program:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<object> list = new List<object> { "This is a string" };
        
        foreach (string entry in list)
        {
            Console.WriteLine(entry);
        }
    }
}

In this case, there's a cast present in the compiled IL. If the list contains something other than a string, it will still compile but will fail at execution time.

1.1.2: Nullity checking in comparisons and defensive coding

In listing 1.4, the comparer doesn't check whether its parameters are null references, nor whether they're of the right type, nor whether the names of the products are null.

How much this matters in real life really depends on what you're doing. You can write defensive code everywhere, knowing that you'll get the most appropriate exception (or "null is less than everything" behaviour), or you can skimp and cope with the consequences. If you're the only one ever to use your types, you can be a bit more relaxed about parameter checking and the like, but when writing libraries for public consumption you should be a lot more careful.

1.1.2: Why return a List<T> from GetSampleProducts?

The GetSampleProducts method in the C# 2 and 3 version of the Product class demonstrates the feature of generics, and collection initializers in C# 3 - but it could do more.

Suppose we'd declared it to return IEnumerable<Product> instead of List<Product>. Then we could have seen iterator blocks with yield return statements. Another major language feature mentioned in the first chapter... So why didn't I do it?

There are two reasons. The first, being totally honest, is that I didn't think of it. It was only when Eric mentioned it at tech review that I saw the possibilities. However, leaving aside the size of change it would have required late on in the editing process, I still don't think it would have been a good idea - simply because iterator blocks scare me slightly. Closures are a bit mind-bending but basically make sense - iterator blocks are a whole extra level of magic.

Don't get me wrong, I love them and use them - but I think that presenting readers with that level of compiler magic that early in the book might be a little off-putting. As soon as you start thinking about the way that at execution time you start dipping into bits of your method at a time, it all gets somewhat bizarre.

Oh, and on a minor point - if the method hadn't returned a List<Product>, it would have been slightly harder to demonstrate sorting in C# 2 :)

1.1.3: List.ForEach - friend or foe?

Eric made an interesting comment on listing 1.11, where I demonstrate the ForEach method. His view is that expressions should be free from side effects wherever possible, and statements should be useful only for their side effects. That suggests that you should use the foreach statement to iterate through a collection and perform actions on it, rather than using an expression with a ForEach call.

Listing 1.12 shows the kind of code Eric is more in favour of, demonstrating (in his words) "shiny beauty". Every statement has a side effect, and every expression computes a value.

This is quite a purist viewpoint I suspect - and to be totally honest, I don't think I'm really qualified to discuss its merits. It's interesting to consider though - try applying it to your code, and see whether the results are clearer.

1.2.1: Why did Sun create Java?

As I note in the book, the operating-system independent Java language was clearly a threat to Microsoft. But imagine if Sun hadn't invented it, but someone else (IBM perhaps?) - would Sun not have regarded it as a threat? After all, it's not only operating-system independent, but also hardware-independent - and Sun is a company selling hardware and operating systems!

Did Sun introduce Java with an eye to hurting Microsoft, without necessarily considering the implications on itself? I don't know - but I'm glad it happened anyway. The effect of Java on the industry has certainly been profound.

1.2.3: Slow adoption rates: sad or not?

When discussing C# 2 and .NET 2.0, I expressed regret that they have taken so long to become widely adopted in the industry. I was reminded that the industry exists to make a profit, not to have fun with technology. New technology often (if not always) involves an element of risk, and there's definitely a lot of sense in sitting back while the early adopters take the risks.

I'm sticking by my use of the word "sad" though - because that hesitation to use .NET 2.0 has left many of us developers "in the trenches" being forced to use .NET 1.1 and C# 1 despite the huge productivity gains available with the later technologies. It's sad for us.

I really, really hope that the multi-targeting feature of Visual Studio 2008 along with the smaller conversion process and lack of a new runtime will mean many of us get to use the updated IDE and language features much earlier than we did last time round - the risk is much lower this time.

1.2.5: Is IronPython really a Trojan horse?

I was perhaps a little harsh using the words "Trojan horse" to describe the way that scripting and dynamic language developers will no doubt find the DLR (Dynamic Language Runtime) attractive. It's not a trick, and there aren't going to be hordes of Microsoft developers leaping out of the DLR when night falls. You may wish to think of it as an alternative entrance to the .NET house instead.

I do still think that it's an enticing prospect, however. Eric pointed out that I was characterising the language as the attractive part of the proposition, whereas he'd say that the framework, OS, and users make it an attractive value proposition. I think it really depends on the developer. Does a developer look at a platform, and consider the lack of her favourite language running on that platform a barrier to entry which can be overcome, or does she only look at the platforms her favourite language is available on? I'm sure there are plenty of developers on each side of the fence - but it's certainly a benefit if that favourite language is available on more platforms, such as .NET.

It's also true that although I point out that the IronPython programmer of today may be the C# programmer of tomorrow, the reverse is true too. By making multiple languages available on the same platform, it's much easier to learn a new language and use the right language in the right context.

These are largely cases of looking at the same picture from different angles. Hopefully we can all agree that having a broad spectrum of languages of different styles (functional, static, dynamic, OO etc) all running on a comprehensive platform is a good thing.

1.4.2: Are using directives really harmless?

Snippy is a simple beast. It has a set of namespaces configured, and will emit a using directive for each of them when compiling code. I claim in the book that this is harmless - but is it really?

In the context of Snippy, it's reasonably okay - but it's worth being aware of the consequences of using directives in C# 3, where they not only introduce types, but also extension methods.

It's fairly easy to come up with examples where the presence or absence of a using directive can drastically affect behaviour - and it's not obvious that this is the case just from the directive itself. Personally I like to trim unnecessary using directives wherever I can in real code.