Bill Blogs in C#

Bill Wagner discusses C#, LINQ, and other items of interest

Of DataBinding and Value Types
Copies and boxes and performance, Oh My.

This caught me and members of a client team recently, so I thought I would share it. In a Windows application, we created a simple value type to store a couple properties. Something like this:

public struct WeatherObservation
{
  private int _low;
  public int Low
  {
    get { return _low ; }
    set { _low = value; }
  }

  private int _high;
  public int High
  {
    get { return _high; }
    set { _high = value; }
  }
}

We had a form that asked the user for input. We utilized Windows Forms Binding:

textBoxLow.DataBindings.Add( "Text", this._weatherData, "Low" );
textBoxHigh.DataBindings.Add( "Text", this._weatherData, "High" );

And, it did not work. The user’s changes never were stored back into the _weatherData structure. Why?

Well, I’m not going to keep you waiting, I’ll dive right into the answer. The problem is that WeatherObservation is a struct, not a class. Structs are value types. They are passed by value, not by reference. More than that, the .NET environment boxes structs to use them where a System.Object is expected.

Here, the method signature for DataBindings.Add is

void Add( string propertyName, object DataSource, string DataMember );

The data source member , a struct, must get boxed in order to match the signature of the Add method. That boxing makes a copy. The databinding infrastructure dutifully modifies the copy whenever the user makes changes. But nowhere are those changes propagated from the copy back to the original object. The bottom line is that structs just can’t participate in read/write databinding. You must use reference types.

One best practice will help you avoid running into this situation yourself. Structs should be immutable. Then, you’ll never expect to use them in read/write situations. This does not prevent you from using members of a struct for read only databinding. The initial values are copied to the control, the same way they are copied when boxed.

You can even use structs as the final storage medium for databainding. When you need to use read/write databinding with a struct, the struct (which should be immutable, remember) should be stored as a member of a reference type. Then, you bind the outer reference type (as the datasource) to the control. The value type can be the datamember, or you can provide access to individual properties of the value type through the outer reference type.

I’ll close by addressing one common question: Why use value types at all? In many cases, the right answer is to avoid value types. It’s certainly simpler. But value types can and do provide some important performance benefits. The small snippet of code above isolated the behavior I wanted to show you, but the actual application was analyzing experiment results for an engineering application. Each experiment run generated several thousand points. The engineers were working with over 500,000 elements in one session. And, the application performs quite a few numerical calculations on all the values to interpret the experiment’s results. Collections that large do exhibit serious performance degradations when all the data are stored in reference types. The memory management issues do matter. Your mileage may vary, and you should always benchmark these kinds of performance issues. But, in general, I’ve found that the value types do offer performance benefits for datasets over 10,000, and then only when you are performing calculations that require you to examine each of the elements individually.



Published Thursday, January 20, 2005 5:50 PM by wwagner
Filed under: ,

Comments

No Comments