2

I created a C# struct which can generically handle case insensitive string comparisons, transparently. So for example:

List<IString> list = new List<IString>();
list.Add("help");
Assert.IsTrue(list.Contains("Help"));

or

Dictionary<IString, string> dict = new Dictionary<IString, string>();
dict["Help"] = "thanks!";
Assert.AreEqual("thanks!", dict["hELP"]);

But the one thing I can't figure out (and maybe it's not possible), is how to get class string to play nicely in .Equals(object):

IString A1 = "A";
string A2 = "a";
Assert.AreEqual(A1, A2, "This passes");
Assert.AreEqual(A2, A1, "This fails");

Here's the code:

using System;
using System.Collections.Generic;
namespace Util
{
 /// <summary>
 /// Case insensitive wrapper for the string class
 /// </summary>
 public struct IString :
 IComparer<IString>,
 IComparable,
 IComparable<IString>,
 IComparable<string>,
 IEquatable<string>,
 IEquatable<IString>
 {
 private const StringComparison icase = StringComparison.OrdinalIgnoreCase;
 public readonly string Value;
 public IString(string Value)
 {
 this.Value = Value;
 }
 public bool Equals(string Other)
 {
 return string.Equals(Value, Other, icase);
 }
 public bool Equals(IString Other)
 {
 return string.Equals(Value, Other.Value, icase);
 }
 public override bool Equals(object obj)
 {
 if (obj is IString || obj is string)
 {
 return string.Equals(Value, obj.ToString(), icase);
 }
 else
 {
 return false;
 }
 }
 public int IndexOf(string Other)
 {
 return Other.IndexOf(Other, icase);
 }
 public bool Contains(string Other)
 {
 return IndexOf(Other) >= 0;
 }
 public override int GetHashCode()
 {
 if (Value == null)
 return 0;
 else
 return StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
 }
 public override string ToString()
 {
 return Value;
 }
 public int Compare(IString x, IString y)
 {
 return string.Compare(x.Value, y.Value, icase);
 }
 public int Compare(string x, string y)
 {
 return string.Compare(x, y, icase);
 }
 public int CompareTo(object obj)
 {
 if (obj is IString)
 return Compare(this, (IString)obj);
 else if (obj is string)
 return Compare(Value, (string)obj);
 else if (Value != null)
 return Value.CompareTo(obj);
 else
 return -1;
 }
 public int CompareTo(IString other)
 {
 return Compare(this, other);
 }
 public int CompareTo(string other)
 {
 return Compare(Value, other);
 }
 public static implicit operator string(IString From)
 {
 return From.Value;
 }
 public static implicit operator IString(string From)
 {
 return new IString(From);
 }
 #region IString to IString operators
 public static bool operator ==(IString Str1, IString Str2)
 {
 return string.Equals(Str1.Value, Str2.Value, icase);
 }
 public static bool operator !=(IString Str1, IString Str2)
 {
 return !string.Equals(Str1.Value, Str2.Value, icase);
 }
 public static IString operator +(IString Str1, IString Str2)
 {
 return (IString)(Str1.Value + Str2.Value);
 }
 public static bool operator >(IString Str1, IString Str2)
 {
 return Str1.CompareTo(Str2) > 0;
 }
 public static bool operator >=(IString Str1, IString Str2)
 {
 return Str1.CompareTo(Str2) >= 0;
 }
 public static bool operator <(IString Str1, IString Str2)
 {
 return Str1.CompareTo(Str2) < 0;
 }
 public static bool operator <=(IString Str1, IString Str2)
 {
 return Str1.CompareTo(Str2) <= 0;
 }
 #endregion IString to IString operators
 #region string to IString operators
 public static bool operator ==(string Str1, IString Str2)
 {
 return string.Equals(Str1, Str2.Value, icase);
 }
 public static bool operator !=(string Str1, IString Str2)
 {
 return !string.Equals(Str1, Str2.Value, icase);
 }
 public static IString operator +(string Str1, IString Str2)
 {
 return (IString)(Str1 + Str2.Value);
 }
 public static bool operator >(string Str1, IString Str2)
 {
 return Str2.CompareTo(Str1) < 0;
 }
 public static bool operator >=(string Str1, IString Str2)
 {
 return Str2.CompareTo(Str1) <= 0;
 }
 public static bool operator <(string Str1, IString Str2)
 {
 return Str2.CompareTo(Str1) > 0;
 }
 public static bool operator <=(string Str1, IString Str2)
 {
 return Str2.CompareTo(Str1) >= 0;
 }
 #endregion string to IString operators
 #region IString to string operators
 public static bool operator ==(IString Str1, string Str2)
 {
 return string.Equals(Str1.Value, Str2, icase);
 }
 public static bool operator !=(IString Str1, string Str2)
 {
 return !string.Equals(Str1.Value, Str2, icase);
 }
 public static IString operator +(IString Str1, string Str2)
 {
 return (IString)(Str1.Value + Str2);
 }
 public static bool operator >(IString Str1, string Str2)
 {
 return Str1.CompareTo(Str2) > 0;
 }
 public static bool operator >=(IString Str1, string Str2)
 {
 return Str1.CompareTo(Str2) >= 0;
 }
 public static bool operator <(IString Str1, string Str2)
 {
 return Str1.CompareTo(Str2) < 0;
 }
 public static bool operator <=(IString Str1, string Str2)
 {
 return Str1.CompareTo(Str2) <= 0;
 }
 #endregion IString to string operators
 }
}

Is there any way to get string.Equal(object) to actually deal with IString as a string?

asked Nov 17, 2010 at 17:02
6
  • You should use OrdinalIgnoreCase instead of InvariantCultureIgnoreCase; it's faster and more secure. Commented Nov 17, 2010 at 17:09
  • Also, return StringComparer.OrdinalIgnoreCase.GetHashCode(Value);. Commented Nov 17, 2010 at 17:09
  • Other than that, and the fact that it's not necessary in the first place, your struct looks pretty good. Commented Nov 17, 2010 at 17:10
  • @SLaks: OK, I'll switch to Ordinal. @CodeInChaos: Yeah, I agree that IString looks like an interface. But part of the reason for wanting this is for brevity, to avoid a bunch of "StringComparison.OrdinalIgnoreCase" stuff all over, to make code more readable. So if someone has a suggestion for a better name, I'm certainly interested. Commented Nov 17, 2010 at 17:45
  • @Bryce: How about InsensitiveString or CaselessString or UncasedString? Commented Nov 17, 2010 at 17:47

2 Answers 2

9

You don't need to create such a type in the first place.
Instead, you should use the StringComparer class.

For example:

var dict = new Dictionary<String, string>(StringComparer.OrdinalIgnoreCase);
dict["Help"] = "thanks!";
Assert.AreEqual("thanks!", dict["hELP"]);

Or

List<String> list = new List<String>();
list.Add("help");
Assert.IsTrue(list.Contains("Help", StringComparer.OrdinalIgnoreCase));

Also note that it shouldn't be named IString; only interfaces should begin with I.

To answer your question, no; that's impossible.

answered Nov 17, 2010 at 17:05
Sign up to request clarification or add additional context in comments.

Comments

2

No there is not. The String.Equals(object) method has a hard dependency on the provided value being of the type String. It does a CLR type check which does not consider any user defined conversions or functions and hence will only work with an instance of System.String

answered Nov 17, 2010 at 17:06

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.