使用逆变 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>()
。但是这两个语句都被编译器拒绝了。
我现在实现的解决方案是一种缓存机制,它会通过反射创建特定的 IEqualityComparer
s,但这似乎有点矫枉过正。
你可以在这里找到例子来玩: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.
虽然这可能与您已有的类似,但这是我要开始的;
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>
方法
我正在尝试合并几个包。包含所有特定结构的 UnitsNet,以及为具有 INotifyPropertyChanged
的属性提供一些不错的 Set<T>
函数的 Microsoft Mvvm。其中一个 Set<T>
函数要求 IEqualityComparer<T>
.
我知道我的 QuantityEqualityComparer
对于来自 UnitsNet 的每个 struct
都是一样的。我看到 IEqualityComparer<in T>
是逆变的。所以我以为我明白了下面的例子应该是可以的。但是这个泛型方法不接受相等比较器。
好像不能把_bar
转成IQuantity
,我也试过调用Set<IQuantity>()
。但是这两个语句都被编译器拒绝了。
我现在实现的解决方案是一种缓存机制,它会通过反射创建特定的 IEqualityComparer
s,但这似乎有点矫枉过正。
你可以在这里找到例子来玩: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.
虽然这可能与您已有的类似,但这是我要开始的;
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>
方法