【问题标题】:Get Default Equality Comparer from Variable Type?从变量类型获取默认相等比较器?
【发布时间】:2021-04-16 22:09:33
【问题描述】:

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

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

这显然不能编译,但如果编译了,comparer 的值应该等于 EqualityComparer&lt;string&gt;.Default 的值

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

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

所讨论的算法是大型旧版搜索 API 的一部分。消费者将对象列表(例如,List&lt;Person&gt;)传递给 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; }
}

【问题讨论】:

  • 你为什么要这样做?
  • 从 EqualityComparer 获得的任何性能都可能因反射开销而丢失。
  • 另外,您的示例无法编译
  • 根据您存储对象的方式,您可以通过将委托传递给Compare() 来加快运行时间,但您仍然需要反射来获得它。
  • 正如其他人所说,不建议为此使用反射。 (你打算如何实际使用 var comparer?更多反射?)FWIW,如果你继续沿着反射路径,你不需要额外的'BoxedComparer'类。这是一个单行代码:var comparer = typeof(EqualityComparer&lt;&gt;).MakeGenericType(RT).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetValue(null);(需要使用 System.Collections.GenericSystem.Reflection 的语句。)

标签: c# .net-4.7.2


【解决方案1】:

致未来的网络搜索者:

反射不是推荐的方法。可能重构是有序的。例如,当var comparer 以这种方式通过反射创建后,它实际上将如何被使用并不清楚。

为了缓解性能问题,您可以在创建比较器时对其进行缓存,就像EqualityComparer&lt;T&gt;source 在内部对每个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(通常您传递包含该属性的对象)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-20
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 2019-01-23
    相关资源
    最近更新 更多