Есть ли лучший способ самостоятельно ссылаться на тип? - PullRequest
3 голосов
/ 23 сентября 2009

В последнее время я нахожу много запахов кода, связанных со ссылками на общие классы в C #. Мои замечания особенно относятся к тем классам, которые наследуются от DependencyObject и содержат DependencyProperties.

Основная проблема заключается в том, что при объявлении свойства зависимости обычно указывается текущий тип, который также известен как owner . Это работает просто отлично и в целом не представляет большой проблемы для простых неуниверсальных объектов, за исключением того, что когда объект содержит несколько свойств зависимостей, а затем имя типа необходимо повторно анализировать в различных местах (это легче сделать с помощью рефакторинга в 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));
}

Что я обнаружил в последнее время, так это то, что при объединении этой громоздкой явной практики самоссылки с широким использованием обобщений код действительно начинает становиться уродливым.

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>));
}

Мой вопрос заключается в том, знает ли кто-нибудь о каких-либо хитростях, хакерских или законных решениях, позволяющих уменьшить количество раз, когда на полное имя типа с общими параметрами необходимо ссылаться в ситуациях, подобных показанной выше.

Полное раскрытие. Я поднял это как проблему на сайте Microsoft .Connect, но они отвергли идею самоссылающегося ключевого слова , но не предложили обходного пути или альтернативного решения. Моя идея заключалась в том, чтобы использовать какое-либо ключевое слово, например Owner , OwnerClass или ThisType , чтобы в общем случае ссылаться на тип, в котором используется ключевое слово.

1 Ответ

3 голосов
/ 23 сентября 2009

Есть несколько вещей, которые вы могли бы сделать, чтобы облегчить боль. Создайте статическую переменную, которая содержит информацию о типе для текущего класса.

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);

Затем вы можете использовать этот хитрый трюк, чтобы сделать ссылки на ваш метод получения и установки безопасными.

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.");
}

Теперь ваш код хотел бы это.

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);
...