I'm Loving the .NET Fx ARM

Saturday, April 10, 2004

I enjoyed the annotations in The .NET Framework Standard Library Annotated Reference so much that I read them all in one sitting (in the bathtub, if you must know...). The annotations are where the authors put in their 2 cents about a particular class, method or property and it was very interesting. Here's what I learned just from that part of the book:

  • You shouldn't use ApplicationException for custom exceptions. Derive from Exception instead.
     
  • Because ICloneable.Clone doesn't define whether it returns a deep or a shallow copy, it's practically worthless.
     
  • Prefer UTF-8 because it takes up the same amount of space to represent ASCII characters as ASCII, but can also represent all Unicode characters (I knew that UTF-8 took up the same amount of space but hadn't yet jumped to "prefer").
     
  • I was reminded of the System.Convert class, which does all of the same conversions that the various type-specific ToXxx and Parse methods do, but puts them all in one place.
     
  • There's another use for the private interface method implementation syntax in C#:
    class Enum : IEnumerator {
      object IEnumerator.Current { get { return this.foo; } }
      ...
    }
    This syntax hides an interface method from general use unless you cast to the interface base class:
    Enum enum = new Enum();
    object obj1 = enum.Current(); // compile-time error
    object obj2 = (IEnumerator)enum.Current(); // this works

    The thing I never thought of that this enables is that it lets you override based on return type, which isn't normally allowed in C#:
    class Enum : IEnumerator {
      object IEnumerator.Current { get { return this.foo; }
    Foo Current { get { return this.foo; } } ... }

    This private method implementation syntax lets you return the generic type as part of the interface implementation so that you can plug into standard idioms, e.g. foreach, but a more specific type for users of the type directly, saving the cast:

    Enum enum2 = new Enum();
    Foo foo1 = (Foo)((IEnumerator)enum.Current());
    Foo foo2 = enum.Current(); // no cast required
    This will be less interesting as generics take over, but still, it's a useful trick.
     
  • DateTime is always assumed to represent local time, so if you convert to Universal time twice, you'll change the value each time.
     
  • Since it's possible to cast any number to an enum value, e.g. TrueFalseEnum tfe = (TrueFalseEnum)42, make sure to always check that an enum is a legal value. Theoretically this is a pain, but in practice, I tend to check enum values with switch statements, making the default case the error case, so it's good to know that my code does the right thing.
     
  • The Threading.Interlocked class.
     
  • Jim Miller has way more birthdays than we do. : )
     
  • Jeff Richter agrees with me that the ThreadStart delegate needs an object parameter.
     
  • I should be using CompareInfo instead of ToLower for case-insensitive comparisons (although they don't show how):
    using System.Globalization;
    using System.Threading;
    ...
    CompareInfo compare = Thread.CurrentThread.CurrentCulture.CompareInfo;
    if( compare.Compare("foo", "FOO", CompareOptions.IgnoreCase) == 0 ) {
    Console.WriteLine("the same"); }

If I can get that much out of just the annotations, imagine what I could learn if I read the whole thing. : )

Discuss