StringComparer.CurrentCultureIgnoreCase在.NET中进行多次调用的效率

时间:2015-06-06 14:50:59

标签: c# .net stringcomparer

我一直在使用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,这是

2 个答案:

答案 0 :(得分:4)

根据reference source,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可再发行组件中省略了Contract.Ensures调用,因此剩余的字段访问几乎肯定会被抖动内联。

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

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

在这个特定的情况下,我通常比较字符串是否相同:

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

现在您根本不必担心StringComparer分配,即使您使用CurrentCulture或CurrentCultureIgnoreCase,代码仍然可以直接阅读。

答案 1 :(得分:1)

永远不要低估使代码线程安全的成本。 CurrentCulture是一个线程的属性,当然不同的线程可以运行不同的文化。您需要一个可以线程安全的方式访问的缓存来存储对象。没有退出策略的缓存是内存泄漏,现在您还必须跟踪上次使用情况以及退出一段时间未使用的对象的方法。没有一个是显而易见的。

在需要时创建对象会更简单,更便宜。它很小,比一根绳子便宜。它不太可能持续很长时间。从gen#0分配的内存未被提升是非常便宜的。

.NET Framework 严重微优化,他们没有摸索这个。