views:

286

answers:

1

I'm finding lately a lot of code smells related to referencing generic classes in C#. My gripes especially apply to those classes which inherit from DependencyObject and contain DependencyProperties.

The basic problem is that when declaring a dependency property one generally references the current type which is also known as the owner. This works just fine and in general isn't much of a problem for simple non-generic objects except for that when the object contains several dependency properties and then the type name needs to be re-factored in various places (made easier with refactoring in Visual Studio).

public class MyDependencyObject : DependencyObject
{
    public int MyProperty
    {
        get { return (int)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty", typeof(int), typeof(MyDependencyObject), new UIPropertyMetadata(0));
}

What I've been finding lately though is that when combining this burdonsome explicit self referencing practice with extensive use of generics the code really starts to become ugly.

public class MyDependencyObject<TypeA, TypeB, TypeC, TypeD> : DependencyObject
{
    public int MyProperty1
    {
        get { return (int)GetValue(MyPropertyProperty1); }
        set { SetValue(MyPropertyProperty1, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty1 =
        DependencyProperty.Register("MyProperty1", typeof(int), typeof(MyDependencyObject<TypeA, TypeB, TypeC, TypeD>));

    public int MyProperty2
    {
        get { return (int)GetValue(MyPropertyProperty2); }
        set { SetValue(MyPropertyProperty2, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty2 =
        DependencyProperty.Register("MyProperty2", typeof(int), typeof(MyDependencyObject<TypeA, TypeB, TypeC, TypeD>));

    public int MyProperty3
    {
        get { return (int)GetValue(MyPropertyProperty3); }
        set { SetValue(MyPropertyProperty3, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty3 =
        DependencyProperty.Register("MyProperty3", typeof(int), typeof(MyDependencyObject<TypeA, TypeB, TypeC, TypeD>));

    public int MyProperty4
    {
        get { return (int)GetValue(MyPropertyProperty4); }
        set { SetValue(MyPropertyProperty4, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty4 =
        DependencyProperty.Register("MyProperty4", typeof(int), typeof(MyDependencyObject<TypeA, TypeB, TypeC, TypeD>));
}

My question is whether anybody is aware of any tricks, hacks, or legitimate solutions to reducing the number of times the full type name with generic parameters needs to be referenced in situations such as the one shown above.

Full disclosure: I did bring this up as an issue on the Microsoft .Connect site but they rejected the idea of a self referencing keyword but offered no workaround or alternative solution. My idea was to use some keyword such as Owner, OwnerClass, or ThisType in order to generically refer to the type in which the keyword is being used.

+3  A: 

There are a couple of things you could do to ease the pain. Create a static variable that contains the type information for the current class.

private static readonly Type ThisType = typeof(MyDependencyObject<TypeA, TypeB, TypeC, TypeD>));

public int MyProperty1
{
  get { return (int)GetValue(MyPropertyProperty1); }
  set { SetValue(MyPropertyProperty1, value); }
}

public static readonly DependencyProperty MyPropertyProperty1 = 
    DependencyProperty.Register("MyProperty1", typeof(int), ThisType);

Next you could use this clever trick to make the references to your getter and setters refactor safe.

private static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
  if (expression.NodeType == ExpressionType.Lambda && expression.BodyType == ExpressionType.MemberAccess)
  {
    PropertyInfo pi = (expression.Body as MemberExpression).Member as PropertyInfo;
    if (pi != null)
    {
      return pi.Name;
    }
  }
  throw new ArgumentException("expression", "Not a property expression.");
}

Now your code would like this.

private static readonly Type ThisType = typeof(MyDependencyObject<TypeA, TypeB, TypeC, TypeD>));

public int MyProperty1
{
  get { return (int)GetValue(MyPropertyProperty1); }
  set { SetValue(MyPropertyProperty1, value); }
}

public static readonly DependencyProperty MyPropertyProperty1 = 
    DependencyProperty.Register(GetPropertyName((MyDependencyObject x) => x.MyProperty1), typeof(int), ThisType);
Brian Gideon
Excellent idea to use the static variable. Two issues still however.1. I can't blankly apply static members across all of my classes or at least those classes which inherit from the some base class, so I'll have to write a variable like this for each DependencyObject I write.2. Another issue is when you need to refer to the type as a generic parameter for some other method or type declaration.All in all I think your solution requires minimal code to produce the static variable and also allows for the flexibility of naming which is pretty nice.
jpierson