0

有没有办法获得给定类型的默认比较器,其中类型是可变的并且仅在运行时知道?考虑以下:

var RT = typeof(string);
var comparer = EqualityComparer<RT>.Default;

这显然不编译,但如果编译,comparer应该有一个等于的值EqualityComparer<string>.Default

我能想到的唯一方法是制作一个可以通过反射调用的“盒装”比较器(见下文)。这行得通,但它很麻烦。有没有更好的方法来做到这一点?

澄清一下,由于反射,这不是一个好主意,但为什么我需要它?

有问题的算法是大型遗留搜索 API 的一部分。消费者将对象列表(例如,List<Person>)传递给 API,在内部创建类型特定的索引(使用反射),以便调用者随后可以搜索该对象中的任何字段(例如,可能是姓氏)。这通常不是必需的,但在我正在服务的用例中,我们正在搜索非常大的集合与其他非常大的集合。为此目的,数据库存储过程可能更好。但是现在我需要修补这个遗留 API 以支持用户定义的比较算法,并且还支持用户选择不提供任何比较算法的情况,我只知道运行时类型RT

// Example usage
// Assume "RT" is a Type known only at runtime (e.g., typeof(string))
var box = typeof(BoxedComparer<>);

var generic = box.MakeGenericType(RT);
var specific = (IBoxedComparer) Activator.CreateInstance(generic);

// Now with specific you can get the equality comparer for the runtime type (RT)
var comparer = specific.GetEqualityComparer();

public interface IBoxedComparer
{
   // You need the interface to allow a "typeless" cast
   EqualityComparer GetEqualityComparer()
}

public BoxedComparer<T> : IBoxedComparer 
{
   public EqualityComparer GetEqualityComparer() { return EqualityComparer<T>.Default; }
}
4

1 回答 1

1

对于未来的网络搜索者:

反射不是执行此操作的推荐方法。可能重构是有序的。例如,不清楚var comparer一旦以这种方式通过反射创建后将如何实际使用。

为了减轻性能问题,您可以在创建比较器时对其进行缓存,就像EqualityComparer<T>每个T. 但是这样你的代码就会变得更加混乱。

那就是说...

一种更简单的单行方式来完成反射:

var comparer = typeof(EqualityComparer<>).MakeGenericType(RT).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetValue(null);

我们在编译时不知道RT的运行时实例在哪里。Type

要求:

using System.Collections.Generic;
using System.Reflection;

请注意,当通过反射访问静态字段或属性时,它应该传递null给 GetValue/SetValue(通常您传递包含该属性的对象)。

同样,此代码不是推荐的方法。可读性、可重用性、维护和错误处理选项并未真正考虑。这个例子只是展示了一种更简单的方法来获得所需的东西,而无需引入额外的类。

于 2021-01-12T01:29:32.913 回答