GetType() weirdness in .NET

Following up this question on stackoverflow I stumbled across some weird issues regarding GetType().

1. GetType() cannot be overridden but hidden

While GetType() is not virtual for very good reasons and therefor one cannot override it the following is possible:

class MyClass
{
    public new Type GetType()
    {
         return typeof(string);
    }
}

Not that this is a good idea but it compiles and runs:

var t1 = new MyClass().GetType();
var t2 = ((object)new MyClass()).GetType();
Console.WriteLine("t1 = {0} --- t2 = {1}", t1.Name, t2.Name);

results in the expected output:

t1 = String --- t2 = MyDisposable

Now if it so important that GetType() does not violate its contract then why hasn’t there been a rule added to the specification saying that you are not allowed to new GetType(). You could argue that GetType() is just a normal method like any other – however it isn’t really. There is a lot of code relying on the fact that it does what it does and is not changed at whim, except it’s still possible to break it under certain circumstances – why not prevent it alltogether? Another argument I guess is that it would assign some special meaning for the compiler to an implemented method on the framework which certainly is not a good idea, right? Well, there are at least two exceptions out there already. One is IDisposable where an interface has a special language construct (using in C#) which relies on it. The other one is Nullable which is the only value type you can assign null to. I admit that one should be careful in what exceptions to the rule are choosen however in the case of GetType() it might have been worth it. Now the latter of the two mentioned exceptions leads me to the second weirdness.

Nullable is only sometimes null

Coming from the linked question at the top, it is apparent that the following is a bit inconsistent:

int? i = null;
Console.WriteLine(i.GetHashCode()); // works
Console.WriteLine(i.ToString()); // works
Console.WriteLine(i.HasValue); // works
Console.WriteLine(i.GetType()); // NullReferenceException

The reason being that GetType() is not virtual and is not overridden and therefor i gets boxed into object resulting in null. So a Nullable set to null does not behave like a reference type set to null when it comes to calling methods on it – except for GetType(). Why that? We have already determined that you can hide GetType() so Nullable could have done just that to and avoided the null reference problem.

Maybe someone can shed some light on why some of the decisions have been as they stand.

Advertisements

ListView with dynamic columns

Quite a while a ago I needed a ListView with columns to be determined at runtime. Using my favourite seach engine I came across an article on codeproject offering a simple enough solution. I also found a thread on stackoverflow to which I posted some remarks. Now someone asked for the whole thing – so I thought it might be a good idea to write it down.

The first thing was that changed the DataMatrix class so that rows are dictionaries mapping column names to cell object instead of relying on the order. I’m also not quite sure what the purpose of the GenericEnumerator class is – why not use the one which the collection already implements? So I got rid of that as well. Last step was to extract an interface so I didn’t have to tie the ListView to a concrete implementation

This is the result:

public interface IDataMatrix : IEnumerable
{
    List<MatrixColumn> Columns { get; set; }
}

public class DataMatrix : IDataMatrix
{
    public List<MatrixColumn> Columns { get; set; }
    public Dictionary<string, Dictionary<string, object>> Rows { get; set; }

    public DataMatrix()
    {
        Columns = new List<MatrixColumn>();
        Rows = new Dictionary<string, Dictionary<string, object>>();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
       return Rows.Values.GetEnumerator();
    }
}

public class MatrixColumn
{
    public string Name { get; set; }
}

In the codeproject article Tawani uses attached properties to add the binding functionality which is quite nice as it makes it a bit more independent. However we already have an ExtendedListView class which incorporates some other changes so I decided to integrate the matrix binding as well. The main changes are that the ColumnHeaderTemplate is copied so you can style the headers and that the display binding is to the column name instead of the index.

    public class ExtendedListView : ListView
    {
        static ExtendedListView()
        {
            ViewProperty.OverrideMetadata(typeof(ExtendedListView), new PropertyMetadata(new PropertyChangedCallback(OnViewPropertyChanged)));
        }

        private static void OnViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateGridView(d as ExtendedListView, (IDataMatrix)d.GetValue(MatrixSourceProperty));
        }

        public static readonly DependencyProperty MatrixSourceProperty =
            DependencyProperty.Register("MatrixSource",
                                                typeof(IDataMatrix), typeof(ExtendedListView),
                                                new FrameworkPropertyMetadata(null,
                                                                              new PropertyChangedCallback(
                                                                                  OnMatrixSourceChanged)));

        public IDataMatrix MatrixSource
        {
            get { return (IDataMatrix)GetValue(MatrixSourceProperty); }
            set { SetValue(MatrixSourceProperty, value); }
        }

        private static void OnMatrixSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var listView = d as ExtendedListView;
            var dataMatrix = e.NewValue as IDataMatrix;

            UpdateGridView(listView, dataMatrix);
        }

        private static void UpdateGridView(ExtendedListView listView, IDataMatrix dataMatrix)
        {
            if (listView == null || listView.View == null || !(listView.View is GridView) || dataMatrix == null)
                return;

            listView.ItemsSource = dataMatrix;
            var gridView = listView.View as GridView;
            gridView.Columns.Clear();
            foreach (var col in dataMatrix.Columns)
            {
                var column = new GridViewColumn
                {
                    Header = col.Name,
                    HeaderTemplate = gridView.ColumnHeaderTemplate,
                    DisplayMemberBinding = new Binding(string.Format("[{0}]", col.Name))
                };
                gridView.Columns.Add(column);
            }
        }
    }

Almost done – you can bind to a DataMatrix and the columns will be automatically generated. The next thing to do was to add customizable cell templates. That proofed a little bit tricky because somehow the data context of the cell always ended up being the whole matrix instead of an individual element. After searching a while for a solution on the web and finding nothing I decided to cheat a little bit and had a look at what’s happening under the hood with reflector. That basically showed that the ContentPresenter is setting the DataContext of the template to it’s own content (which is the matrix). So I added a wrapper to set the content of the presenter to the actual object instead of the whole matrix and then letting the presenter do its magic to pass it on to the template. It’s a bit ugly and relies on an undocumented behaviour so it might break in the future but so far (up to .Net 4.0) it still works.

        public class DataMatrixCellTemplateSelectorWrapper : DataTemplateSelector
        {
            private readonly DataTemplateSelector _ActualSelector;
            private readonly string _ColumnName;
            private Dictionary _OriginalRow;

            public DataMatrixCellTemplateSelectorWrapper(DataTemplateSelector actualSelector, string columnName)
            {
                _ActualSelector = actualSelector;
                _ColumnName = columnName;
            }

            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                // remember old data context
                if (item is Dictionary)
                {
                    _OriginalRow = item as Dictionary;
                }

                if (_OriginalRow == null)
                    return null;

                // get the actual cell object
                var obj = _OriginalRow[_ColumnName];

                // select the template based on the cell object
                var template = _ActualSelector.SelectTemplate(obj, container);

                // find the presenter and change the content to the cell object so that it will become
                // the data context of the template
                var presenter = Utils.GetFirstParentForChild(container);
                if (presenter != null)
                {
                    presenter.Content = obj;
                }

                return template;
            }
        }

The only bit missing is to add a CellTemplateSelector to the list view and we are done.

 public class ExtendedListView : ListView
    {
        static ExtendedListView()
        {
            ViewProperty.OverrideMetadata(typeof(ExtendedListView), new PropertyMetadata(new PropertyChangedCallback(OnViewPropertyChanged)));
        }

        private static void OnViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateGridView(d as ExtendedListView, (IDataMatrix)d.GetValue(MatrixSourceProperty));
        }

        public static readonly DependencyProperty MatrixSourceProperty =
            DependencyProperty.Register("MatrixSource",
                                                typeof(IDataMatrix), typeof(ExtendedListView),
                                                new FrameworkPropertyMetadata(null,
                                                                              new PropertyChangedCallback(
                                                                                  OnMatrixSourceChanged)));

        public IDataMatrix MatrixSource
        {
            get { return (IDataMatrix)GetValue(MatrixSourceProperty); }
            set { SetValue(MatrixSourceProperty, value); }
        }

        private static void OnMatrixSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var listView = d as ExtendedListView;
            var dataMatrix = e.NewValue as IDataMatrix;

            UpdateGridView(listView, dataMatrix);
        }

        public static readonly DependencyProperty CellTemplateSelectorProperty =
           DependencyProperty.Register("CellTemplateSelector",
                                               typeof(DataTemplateSelector), typeof(ExtendedListView),
                                               new FrameworkPropertyMetadata(null,
                                                                             new PropertyChangedCallback(
                                                                                 OnCellTemplateSelectorChanged)));

        public DataTemplateSelector CellTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(CellTemplateSelectorProperty); }
            set { SetValue(CellTemplateSelectorProperty, value); }
        }

        private static void OnCellTemplateSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var listView = d as ExtendedListView;
            if (listView != null)
            {
                UpdateGridView(listView, listView.MatrixSource);
            }
        }

        private static void UpdateGridView(ExtendedListView listView, IDataMatrix dataMatrix)
        {
            if (listView == null || listView.View == null || !(listView.View is GridView) || dataMatrix == null)
                return;

            listView.ItemsSource = dataMatrix;
            var gridView = listView.View as GridView;
            gridView.Columns.Clear();
            foreach (var col in dataMatrix.Columns)
            {
                var column = new GridViewColumn
                {
                    Header = col.Name,
                    HeaderTemplate = gridView.ColumnHeaderTemplate
                };
                if (listView.CellTemplateSelector != null)
                {
                    column.CellTemplateSelector = new DataMatrixCellTemplateSelectorWrapper(listView.CellTemplateSelector, col.Name);
                }
                else
                {
                    column.DisplayMemberBinding = new Binding(string.Format("[{0}]", col.Name));
                }
                gridView.Columns.Add(column);
            }
        }
    }

The code is by no means perfect – there are always things which could be improved:

  • Encapsulate the DataMatrix better and give it a nicer interface
  • Instead of having to use a CellTemplateSelector it would be nice if the templates could be selected by DataType
  • Make the matrix columns observable and react to dynamic changes
  • Have a bit mor intelligent update mechanism than to rebind the whole matrix

You can download the whole solution here: DataGridListView.zip

Value types and null in C#

Recently I came across one of the edge cases in C#. I needed a method to combine a list of items to a string. So I wrote an extension method for that:

public static string StringJoin<T>(this IEnumerable<T> list, string separator, Func<T, string> converter)
{
    return string.Join(separator, list.Select(converter).ToArray());
}

Pretty simple: takes a separator and a delegate to convert objects of type T into string. Next thing was to add a convenience method using ToString as default converter.

public static string StringJoin<T>(this IEnumerable<T> list, string separator)
{
    return list.StringJoin(separator, x => x != null ? x.ToString() : null);
}

The null check is to make sure I don’t get unexpected null reference exceptions. However ReSharper gave me a warning about a possible comparison of value type with null. Furthermore it offered me the suggestion to replace it with default(T) and indeed when letting it do its magic it would convert it to

public static string StringJoin<T>(this IEnumerable<T> list, string separator)
{
    return list.StringJoin(separator, x => x != default(T)? x.ToString() : null);
}

which is wrong – it won’t compile.

So I got curious why the ReSharper guys went through the motions to not only warn about it (well – it’s a compiler warning, so I guess it’s fair enough) but to add a suggestion which leads to uncompilable code. I asked my favourite search engine about it and I came across a few articles about that “problem” all basically stating that’s how it is. And someone came up with a solution here: http://devnet.jetbrains.net/thread/293148?tstart=0

After writing some benchmark it turned out that in release mode the workaround was about 5 times slower than the simple comparison.

What do we learn from it this?

  1. Don’t optimize prematurely.
  2. Don’t try to fix what is not broken.
  3. Don’t try to outsmart the compiler – it might cost you in optimization potential.