使用逆变 IEqualityComparer<T> 调用泛型函数

Calling a generic function with a contravariant IEqualityComparer<T>

我正在尝试合并几个包。包含所有特定结构的 UnitsNet,以及为具有 INotifyPropertyChanged 的属性提供一些不错的 Set<T> 函数的 Microsoft Mvvm。其中一个 Set<T> 函数要求 IEqualityComparer<T>.

我知道我的 QuantityEqualityComparer 对于来自 UnitsNet 的每个 struct 都是一样的。我看到 IEqualityComparer<in T> 是逆变的。所以我以为我明白了下面的例子应该是可以的。但是这个泛型方法不接受相等比较器。

好像不能把_bar转成IQuantity,我也试过调用Set<IQuantity>()。但是这两个语句都被编译器拒绝了。

我现在实现的解决方案是一种缓存机制,它会通过反射创建特定的 IEqualityComparers,但这似乎有点矫枉过正。

你可以在这里找到例子来玩:https://dotnetfiddle.net/N7vfc9

using System;
using System.Collections.Generic;
                    
public class Program
{
    private static Length _bar;
    
    public static void Main()
    {
        // Error: cannot convert from QuantityEqualityComparer to IEqualityComparer<Length>
        Set(ref _bar, new Length(), new QuantityEqualityComparer());
    }
    
    // from Mvvm
    public static bool Set<T>(ref T field, T value, IEqualityComparer<T> comparer)
    {
        return true;
    }
}
            
public class QuantityEqualityComparer : IEqualityComparer<IQuantity>
{
    public bool Equals(IQuantity x, IQuantity y)
    {
        // custom implementation
    }
    
    public int GetHashCode(IQuantity obj)
    {
        // custom implementation
    }
}

// from UnitsNet
public interface IQuantity
{
}

public struct Length : IQuantity, IEquatable<Length>
{
    public bool Equals(Length other)
    {
        return true;
    }
}

调用您的 QuantityEqualityComparer.Equals 方法需要装箱长度结构,这就是逆变转换失败的原因。

Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.

source

虽然这可能与您已有的类似,但这是我要开始的;

public static bool Set<T>(ref T field, T value)
    where T:IQuantity
=> Set<T>(ref field, value, QuantityEqualityComparer<T>.Instance);

public class QuantityEqualityComparer<T> : IEqualityComparer<T>
where T:IQuantity
{
    public static QuantityEqualityComparer<T> Instance = new();
    //...
}

然后使用反射或者.Compile表达式树来调用这个.Set<T>方法