class C# 泛型中的特定函数 - 轻微 mod 到 UWP 模板 Observable

class specific function within C# generics - slight mod to UWP template Observable

我正在使用最新的 UWP(和 Windows Template Studio)方法构建应用程序。有一个聪明的 class 叫做 "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));
    }

这不起作用,因为它显示 "cannot convert T to double"。有办法解决这个问题吗?

好了,我的问题有了答案。你知道,泛型很难理解。如果以下不是处理此问题的方法,请发表评论。需要的是添加具有双精度输入签名的 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));
    }

提问者贴出的答案一般认为是正确的做法。当你有一个泛型方法必须为一种特定类型做一些特殊的事情时,它不再是 generic

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

This is not working because it says "cannot convert T to double". Is there a way to fix this?

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

首先,这通常是一种糟糕的类型测试方式。

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(...

因为这会受到拳击处罚;抖动不一定足够聪明,可以优化掉 double -> object -> double 步骤,这很昂贵。


虽然正如我所说,如果可能的话,一般来说专攻是个好主意,请考虑以下情况。如果您专注于制作一个可以双打的版本和一个可以完成其他所有功能的版本,那么:

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>

所以要小心。