StringComparer.CurrentCultureIgnoreCase在.net中多次调用的效率

本文关键字:调用 效率 CurrentCultureIgnoreCase net StringComparer | 更新日期: 2023-09-27 17:54:13

我一直使用StringComparer.CurrentCultureIgnoreCase进行不区分大小写的比较和散列。但是在检查参考源后,我看到它创建了一个新的实例与每次调用(它不应该是一个静态函数吗?只是为了形式的缘故)。不管怎样,我的问题是,当你有多个比较要做时,像IEquality<T>实现,这样做是否有效:

// 2 instances per call
return StringComparer.CurrentCultureIgnoreCase.Equals(this.a, other.a)
  && StringComparer.CurrentCultureIgnoreCase.Equals(this.b, other.b) .. etc ..

或者:

public bool Equals(MyObj other)
{
  // 1 instance per call
  var equ = StringComparer.CurrentCultureIgnoreCase;
  return equ.Equals(this.a, other.a)
    && equ.Equals(this.b, other.b) .. etc ..
}

或者甚至缓存/池比较器,所以他们不创建每次Equals()被调用?

// 1 instance per thread
[ThreadStatic]
private static StringComparer equ;
public bool Equals(MyObj other)
{
  if (equ == null) equ = StringComparer.CurrentCultureIgnoreCase;
  return equ.Equals(this.a, other.a)
    && equ.Equals(this.b, other.b) .. etc ..
}

你觉得哪个是最佳实践吗?

(感谢michael-liu指出OrdinalIgnoreCase不是一个新实例,我已经切换到CurrentCultureIgnoreCase,这是)

StringComparer.CurrentCultureIgnoreCase在.net中多次调用的效率

根据引用源,OrdinalIgnoreCase每次都返回相同的静态实例:

public abstract class StringComparer : ...
{
    ...
    private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true);        
    ...
    public static StringComparer OrdinalIgnoreCase { 
        get {
            Contract.Ensures(Contract.Result<StringComparer>() != null);
            return _ordinalIgnoreCase;
        }
    }

自合同生效。确保调用在实际的。net可重发行版中被省略,剩余的字段访问几乎肯定会被jitter内联。

(同样适用于InvariantCulture, InvariantCultureIgnoreCase和Ordinal.)

另一方面,CurrentCulture和CurrentCultureIgnoreCase 确实在每次访问它们时返回新的实例,因为当前的文化可能在访问之间发生变化。在这种情况下应该缓存比较器吗?就我个人而言,我不会让我的代码变得更复杂,除非分析表明存在问题。

在这个特殊的情况下,我通常会像这样比较字符串是否相等:

return String.Equals(this.a, other.a, StringComparison.OrdinalIgnoreCase);

现在,即使使用CurrentCulture或CurrentCultureIgnoreCase,您也完全不必担心StringComparer的分配,并且代码仍然很容易阅读。

永远不要低估使代码线程安全的代价。CurrentCulture是线程的一个属性,当然不同的线程可以使用不同的区域性运行。您需要一个可以以线程安全的方式访问的缓存来存储对象。没有退役策略的缓存是内存泄漏,现在您还必须跟踪最后使用情况,并找到一种方法来退役一段时间未使用的对象。

在需要时创建对象要简单得多,也便宜得多。它很小,比绳子便宜。它不太可能持续太久。从gen #0分配的内存不被提升是非常便宜的。

. net Framework 进行了大量的微优化,他们没有搞砸这个