2

我正在使用最新的 UWP(和 Windows Template Studio)方法构建应用程序。有一个聪明的类叫做"Observable"。所以,这只是背景。我想修改第 13 行的条件,以便双打的可忽略不计的更改不会标记属性更改。所以,我增加了第 13 行并创建了一个名为 NegligibleChange 的新函数......

    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 转换为双精度”。有没有办法来解决这个问题?

4

2 回答 2

3

提问者发布的答案通常被认为是正确的方法。当你有一个泛型方法必须为一种特定类型做一些特殊的事情时,它就不再是泛型的。

也就是说,首先,实际问题从未得到回答,其次,关于这种方法有一些注意事项需要考虑。

这不起作用,因为它说“无法将 T 转换为双精度”。有没有办法来解决这个问题?

是的,有几种方法可以做到这一点,其中一些方法比其他方法更好。

首先,这通常是一种不好的类型测试方法。

typeof(T) == typeof(double)

您手头有两个 T,因此您可以这样做:

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>按要求调用。

所以要小心。

于 2018-09-28T23:00:32.910 回答
1

好吧,我有自己的问题的答案。你知道,泛型很难理解。如果以下不是处理此问题的方法,请发表评论。需要添加具有双精度输入签名的 Set 函数的非泛型版本。这样在运行时就没有额外的条件。

    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));
    }
于 2018-09-28T22:48:34.430 回答