System.String如何正确包装以防止不区分大小写?

Ðаn*_*Ðаn 8 .net c# string

这个问题不是关于管理Windows路径名; 我仅将其用作不区分大小写的字符串的特定示例.(如果我现在改变这个例子,那么一大堆评论将毫无意义.)


这可能类似于可能创建不区分大小写的字符串类?,但那里没有很多讨论.此外,我并不真正关心所string享有的紧密语言集成或性能优化System.String.

比方说,我用了很多这些(正常)不区分大小写Windows路径名的(我不是真正关心的实际像路径的很多细节\/,\\\\是一样的\,file://网址..等).一个简单的包装器可能是:

sealed class WindowsPathname : IEquatable<WindowsPathname> /* TODO: more interfaces from System.String */
{
    public WindowsPathname(string path)
    {
        if (path == null) throw new ArgumentNullException(nameof(path));
        Value = path;
    }

    public string Value { get; }

    public override int GetHashCode()
    {
        return Value.ToUpperInvariant().GetHashCode();
    }

    public override string ToString()
    {
        return Value.ToString();
    }

    public override bool Equals(object obj)
    {
        var strObj = obj as string;
        if (strObj != null)
            return Equals(new WindowsPathname(strObj));

        var other = obj as WindowsPathname;
        if (other != null)
            return Equals(other);

        return false;
    }
    public bool Equals(WindowsPathname other)
    {
        // A LOT more needs to be done to make Windows pathanames equal.
        // This is just a specific example of the need for a case-insensitive string
        return Value.Equals(other.Value, StringComparison.OrdinalIgnoreCase);
    }
}
Run Code Online (Sandbox Code Playgroud)

是的,可能应该实现System.String上的所有/大多数接口; 但上述内容似乎足以供讨论之用.

我现在可以写:

var p1 = new WindowsPathname(@"c:\foo.txt");
var p2 = new WindowsPathname(@"C:\FOO.TXT");
bool areEqual = p1.Equals(p2); // true  
Run Code Online (Sandbox Code Playgroud)

这允许我WindowsPathname在我的代码中"讨论" 而不是像实现细节那样StringComparison.OrdinalIgnoreCase.(是的,这个特定的类也可以扩展为处理\vs,/这样c:/foo.txt将等于C:\ FOO.TXT ;但这不是这个问题的重点.)此外,这个类(带有额外的接口) )当实例添加到集合时,将不区分大小写; 没有必要指定一个IEqualityComparer.最后,像这样的特定类也可以更容易地防止"无意义"操作,例如将文件系统路径与注册表项进行比较.

问题是:这种方法会成功吗?是否有任何严重和/或微妙的缺陷或其他"陷阱"?(再次,与尝试设置不区分大小写的字符串类,而不是管理Windows路径名有关.)

小智 8

我将创建一个包含字符串的不可变结构,将构造函数中的字符串转换为标准大小写(例如小写).然后,您还可以添加隐式运算符以简化创建并覆盖比较运算符.我认为这是实现行为的最简单方法,而且只需要很小的开销(转换仅在构造函数中).

这是代码:

public struct CaseInsensitiveString
{
    private readonly string _s;

    public CaseInsensitiveString(string s)
    {
        _s = s.ToLowerInvariant();
    }

    public static implicit operator CaseInsensitiveString(string d)
    {
        return new CaseInsensitiveString(d);
    }

    public override bool Equals(object obj)
    {
        return obj is CaseInsensitiveString && this == (CaseInsensitiveString)obj;
    }

    public override int GetHashCode()
    {
        return _s.GetHashCode();
    }

    public static bool operator ==(CaseInsensitiveString x, CaseInsensitiveString y)
    {
        return x._s == y._s;
    }

    public static bool operator !=(CaseInsensitiveString x, CaseInsensitiveString y)
    {
        return !(x == y);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是用法:

CaseInsensitiveString a = "STRING";
CaseInsensitiveString b = "string";

// a == b --> true
Run Code Online (Sandbox Code Playgroud)

这也适用于收藏.

  • 除了不变性之外,还要考虑性能,`struct`也在这里获胜; 它是一个围绕单个引用/指针的轻量级包装器,复制起来很便宜(可能和引用本身一样便宜),但不会受到"class"所需的额外级别的间接性的影响. (3认同)