специфичная для класса функция в C # generics - небольшой мод для шаблона UWP Observable - PullRequest
0 голосов
/ 28 сентября 2018

Я создаю приложение с использованием новейшего подхода UWP (и Windows Template Studio).Есть умный класс с именем "Observable" .Так что это просто фон.Я хочу изменить условие в строке 13, чтобы незначительные изменения для двойников не помечали изменение свойства.Итак, я дополнил эту строку-13 и создал новую функцию с именем NeglitableChange ...

    protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
    {
        if ((typeof(T) == typeof(double) && NegligibleChange(storage, value)) || Equals(storage, value))
        {
            return;
        }
        storage = value;
        OnPropertyChanged(propertyName);
    }

    private bool NegligibleChange(double  x, double y)
    {
        return Math.Abs(x - y) <= 1e-10;
    }

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

Это не работает, потому что он говорит «не может преобразовать T в удвоение».Есть ли способ это исправить?

Ответы [ 2 ]

0 голосов
/ 29 сентября 2018

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

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

Это не работает, поскольку в нем говорится "не может преобразовать"Т, чтобы удвоить ".Есть ли способ исправить это?

Да, есть несколько способов сделать это, и некоторые из них лучше, чем другие.

Во-первых, это вВообще, плохой способ набрать test.

typeof(T) == typeof(double)

У вас есть два Ts в руке, поэтому вместо этого вы должны сделать следующее:

protected void Set<T>(ref T storage, T value, blah blah blah)
{
    if (Equals(storage, value))
        return;
    double? oldValue = storage as double?;
    double? newValue = value as double?;
    if (oldValue != null && newValue != null && Negligible(oldValue.Value, newValue.Value))
      return;
    ...

Обратите внимание, что плохой способ сделать это:

protected void Set<T>(ref T storage, T value, blah blah blah)
{
    if (Equals(storage, value))
        return;
    if (storage is double && value is double) 
    {
      double oldValue = (double)(object)storage;
      double newValue = (double)(object)value;
      if (Negligible(...

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


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

Set(ref someDouble, 1.23)

вызовет двойную версию, но

Set<double>(ref someDouble, 1.23)

будет по-прежнему вызыватьуниверсальная версия .C # предпочитает неуниверсальную версию универсальной версии, но если вы явно запросите универсальную версию, вы получите ее.

Точно так же, если вы звонили из общего контекста:

class C<T> 
{
  T storage;
  void Frob(Blah blah, T value) {
     blah.Set<T>(ref storage, value);
  }

Тогда C<double> не вызывает вашу специальную версию;опять же, это вызывает Set<double> в соответствии с просьбой.

Так что будьте осторожны.

0 голосов
/ 29 сентября 2018

Хорошо, у меня есть ответ на мою проблему.Знаете, дженерики сложно понять.Пожалуйста, прокомментируйте, если следующее не способ решить эту проблему.Необходимо добавить неуниверсальную версию функции Set с входными сигнатурами типа double.Таким образом, нет никаких дополнительных условий во время выполнения.

    protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return;
        }
        storage = value;
        OnPropertyChanged(propertyName);
    }
    protected void Set(ref double storage, double value, [CallerMemberName]string propertyName = null)
    {
        if (NegligibleChange(storage, value))
        {
            return;
        }
        storage = value;
        OnPropertyChanged(propertyName);
    }

    private bool NegligibleChange(double  x, double y)
    {
        return Math.Abs(x - y) <= 1e-10;
    }

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
...