Which StringComparison? Ordinal!

I’ve never had to deal with localization, so I haven’t put much thought into the various .NET internationalization features. In the case of StringComparison, I should have done my homework. I’ve seen code samples that use any combination of CurrentCulture, InvariantCulture and Ordinal, but MSDN is very clear: Ordinal and OrdinalIgnoreCase are almost always the right choice. This is especially true for strings like URLs that should never have non-ASCII characters anyway.

The specific MSDN recommendations are worth repeating:

  • DO: Use StringComparison.Ordinal or OrdinalIgnoreCase for comparisons as your safe default for culture-agnostic string matching.
  • DO: Use StringComparison.Ordinal and OrdinalIgnoreCase comparisons for increased speed.
  • DO: Use StringComparison.CurrentCulture-based string operations when displaying the output to the user.
  • DO: Switch current use of string operations based on the invariant culture to use the non-linguistic StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase when the comparison is linguistically irrelevant (symbolic, for example).
  • DO: Use ToUpperInvariant rather than ToLowerInvariant when normalizing strings for comparison.
  • DON’T: Use overloads for string operations that don’t explicitly or implicitly specify the string comparison mechanism.
  • DON’T: Use StringComparison.InvariantCulture-based string operations in most cases; one of the few exceptions would be persisting linguistically meaningful but culturally-agnostic data.

First, note that Ordinal comparisons are significantly faster, “essentially a [byte-wise] C runtime strcmp.”

More importantly, note the recommendation to specify the comparison mechanism whenever possible, as different methods have different default behavior. In BCL 2.0, String.Equals is Ordinal by default, but the majority (Compare, IndexOf, StartsWith, etc) use CurrentCulture. InfoQ recently reported that these defaults will change in .NET 4.0; in fact, the shift has already started with BCL 2.0.5 that shipped with Silverlight 2.0.

For example, in mscorlib, Version=2.0.0.0:

public int IndexOf(string value)
{
    return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value);
}

But in Silverlight’s mscorlib, Version=2.0.5.0:

public int IndexOf(string value)
{
    return this.IndexOf(value, StringComparison.Ordinal);
}

So be careful porting string-manipulation code for “linguistically meaningful but culturally-agnostic data,” and other data too, into Silverlight. If you are at all interested in internationalization, the MSDN article is definitely worth a read. And even if you’re not, at least remember to specify StringComparison types.

Advertisements

INotifyPropertyChanged Dependency Properties for Silverlight PathTextBlock

I’ve had my eye on Silverlight for a while, but haven’t had much reason to do anything with it. However, I recently stumbled on Bill Reiss‘s PathTextBlock project (via Tim Heuer) and thought I’d try to help out as an excuse to dabble. In addition to implementing a naive 3D projection transform, I wanted to add support for bindable dependency properties. It was harder than it should have been, but the eventual solution turned out to be pretty elegant so I thought I’d share it here.

Defining a DependencyProperty

If you’re not familiar, the general stub for a DependencyProperty in MyClass looks something like this:

  public static readonly DependencyProperty MyStringProperty =
      DependencyProperty.Register(
          "MyString",
          typeof(string),
          typeof(MyClass),
          new PropertyMetadata("My Default Value", MyClass.OnPropertyChanged));

  public string MyString {
    get { return (string)GetValue(MyStringProperty); }
    set { SetValue(MyStringProperty, value); }
  }

  protected static void OnPropertyChanged(DependencyObject d,
      DependencyPropertyChangedEventArgs e) { ... }

For more information on dependency properties, check out this article on MSDN. Note that the "MyString" parameter is the name of the property and that DependencyProperty has a public Name property (in .NET 3.0).

Implementing INotifyPropertyChanged

Now that MyString is a DependencyProperty, we just need to implement INotifyPropertyChanged on MyClass. In WPF, that could look something like this:

  public event PropertyChangedEventHandler PropertyChanged;

  protected static void OnPropertyChanged(DependencyObject d,
      DependencyPropertyChangedEventArgs e) {
    var m = d as MyClass;
    if (m != null) {
      DependencyProperty p = e.Property;
      if (pcdo.PropertyChanged != null)
        pcdo.PropertyChanged(p, new PropertyChangedEventArgs(p.Name));
    }
  }

Unfortunately, the .NET 3.0 implementation wasn’t good enough for Silverlight. In the Silverlight version of DependencyProperty, the Name property is nowhere to be found! It’s actually still there, it’s just internal for some reason. Since we can’t retrieve the name from the property object, I use a dictionary to cache the names instead. At the same time, let’s define a few helper methods to hide the dictionary:

  private static Dictionary<DependencyProperty, string> props =
      new Dictionary<DependencyProperty, string>();

  protected static DependencyProperty Register(
      string name,
      Type propertyType,
      Type ownerType,
      object defaultValue) {
    return Register(name, propertyType, ownerType, defaultValue, OnPropertyChanged);
  }

  protected static DependencyProperty Register(
      string name,
      Type propertyType,
      Type ownerType,
      object defaultValue,
      PropertyChangedCallback callback) {
    DependencyProperty prop =
        DependencyProperty.Register(
            name,
            propertyType,
            ownerType,
            new PropertyMetadata(defaultValue, callback));
    props.Add(prop, name);
    return prop;
  }

  protected static void OnPropertyChanged(DependencyObject d,
      DependencyPropertyChangedEventArgs e) {
    var m = d as MyClass;
    if (m != null) {
      DependencyProperty p = e.Property;
      string propertyName = props.ContainsKey(p) ? props[p] : "Unknown";
      if (m.PropertyChanged != null)
        m.PropertyChanged(p, new PropertyChangedEventArgs(propertyName));
    }
  }

And we can use our new Register method to initialize our properties:

  public static readonly DependencyProperty MyStringProperty =
      Register("MyString", typeof(string), typeof(MyClass), "My Default Value");
  public static readonly DependencyProperty MyBoolProperty =
      Register("MyBool", typeof(bool), typeof(MyClass), true);

<Rant>

I really wish MS would lay off the internal access modifier. This case was pretty easy to work around, but there are other instances where it’s a significant barrier. For example, the PathTextBlock project defines its own Transform and TransformGroup classes that are functionally identical to those provided by the SL-FCL. The FCL’s GeneralTransform and Transform classes are even public…too bad their only constructor is internal!! So rather than build on the existing framework, the wheel gets reinvented. Grrr!

</Rant>

Frustrations aside, the solution works really well for data-bindable dependency properties. For PathTextBlock, I built the above code into a generic PropertyChangedDependencyObject class from which to inherit. One thing to note: if a subclass hides the base OnPropertyChanged method, like I do in Transform, the property registration needs a reference to the new OnPropertyChanged. Feel free to check out the latest source on CodePlex for the full working sample.