【问题标题】:String Compare where null and empty are equal字符串比较 null 和 empty 是否相等
【发布时间】:2010-12-20 07:51:04
【问题描述】:

使用 C# 和 .NET 3.5,处理这种情况的最佳方法是什么。我有数百个字段可以从各种来源(主要是字符串)进行比较。有时源将字符串字段返回为 null,有时返回为空。当然,有时字段中有文本。例如,我当前对 strA != strB 的比较并没有削减它,因为 strA 为 null 而 strB 为“”。我知道我可以执行 string.IsNullOrEmpty ,这会导致双重比较和一些丑陋。有没有更好的方法来处理这个?我想到了扩展方法,但你不能扩展运算符。

我想我正在寻找一种性感的方式来做到这一点。

【问题讨论】:

  • 你不能让“SOURCE”返回空字符串,还是希望有一个空字符串可用?

标签: c#


【解决方案1】:

不会消除额外的潜在比较,但对于性感因素,您可以使用以下内容:

(strA ?? "") == (strB ?? "")

或稍微不那么性感但更可取的形式:

(strA ?? string.Empty) == (strB ?? string.Empty)

【讨论】:

  • 好吧,我试图用扩展方法重做数百次比较,但我想我将不得不硬着头皮。我最终可能会编写一个在幕后执行此操作的方法。谢谢。
  • string.Empty 不像 相当 那样性感(不那么紧凑),这就是我将两者都包括在内的原因,但是是的,使用 string.Empty 是首选。
  • "string.Empty 是首选"
  • 该死!在错误的时刻按回车键。意思是说:“string.Empty 是首选”为什么?它更长,更丑陋,并且对运行时行为没有影响。请参阅有关 String.Empty 和 link 的其他 SO 条目。就我而言,“”是完全可读的,比 String.Empty 更可取。
  • 可读吗?可能是。优选?不总是。 string.Empty 不分配用于实习的新的不可变字符串实例(这可能等于每个程序集或每个 AppDomain 的一个附加对象引用,不确定其中任何一个是否为真)。就原始性能而言(在那篇文章发表时以及在测试的机器/运行时上),有一些替代方案可以产生更好的结果。我更喜欢 string.Empty > String.Empty > "" 只是因为我更喜欢 "string" 而不是 "String" 并且内联常量 ("") 通常会被放入 const 中,而我的重构感觉会很刺痛!!
【解决方案2】:

怎么样

strA ?? "" == strB ?? ""

【讨论】:

    【解决方案3】:

    string.IsNullOrEmpty() 有什么问题?我敢肯定,由于它是 .NET 框架的一部分,因此它已经过优化,并且可能比您或我编写的内容效率更高。它可能不性感,但它有效。编写易于阅读的代码,让编译器整理出细节。

    【讨论】:

    • 因为有两个检查,我现在必须用所有这些噪音替换数百个比较。
    • 这可能是,但找不到替代品可以帮助您更新代码吗?
    • 当然,但您不打算替换数百个比较吗?您打算如何在不手动操作的情况下替换 !=
    • +1 表示“编写易于阅读的代码并让编译器整理出细节”。问题看起来像一个过早的优化恕我直言
    • @billb:性感快还是性感漂亮? if ((s1 ?? "") == (s2 ?? "")) 的 10,000,000 次迭代大约需要 155 毫秒,其中 if (string.IsNullOrEmpty(s1) && string.IsNullOrEmpty(s2)) 的 10,000,000 次迭代需要 145 毫秒(其中一个字符串为空,另一个为空)。我同意这种差异不值得担心,我只是指出“更好”总是在竞争要求的背景下。
    【解决方案4】:

    不像??那么性感,但如果你把它短路,你可以避免双重比较:

    string.IsNullOrEmpty( strA ) ? string.IsNullOrEmpty( strB ) : (strA == strB )
    

    【讨论】:

      【解决方案5】:

      由于您需要进行数百次比较,听起来您希望调用一个函数,以便减少代码中的混乱和重复。我不认为有一个内置函数可以将空/空字符串/比较检查全部合二为一,但您可以自己制作一个:

      static class Comparison
      {
          public static bool AreEqual(string a, string b)
          {
              if (string.IsNullOrEmpty(a))
              {
                  return string.IsNullOrEmpty(b);
              }
              else
              {
                  return string.Equals(a, b);
              }
          }
      }
      

      然后您可以只对您的函数进行一次调用来进行每次比较:

              if(Comparison.AreEqual(strA[0], strB[0])) { // ... }
              if(Comparison.AreEqual(strA[1], strB[1])) { // ... }
              if(Comparison.AreEqual(strA[2], strB[2])) { // ... }
              if(Comparison.AreEqual(strA[3], strB[3])) { // ... }
      

      如果您后来发现需要担心其他情况,例如忽略字符串开头或结尾的空格,这种方法也更容易扩展;然后,您可以在函数中添加更多逻辑来进行一些修剪或其他操作,而无需对调用函数的数百行代码进行任何修改。

      【讨论】:

      • String.Equals(a,b) 也处理空比较,因此可以摆脱 string.IsNullOrEmpty() 检查msdn.microsoft.com/en-us/library/1hkt4325(v=vs.90).aspx
      • @abhaybhatia String.Equals(a,b) 不认为 null 等于空字符串,这是所期望的。因此,string.IsNullOrEmpty 检查是必要的。
      【解决方案6】:

      如果您的 2 组字段位于某种集合中,则可以使用 LINQ 来发挥您的优势。如果它们在某种允许您通过密钥访问它们的集合中并且它们都具有相同的密钥,则可以使用它(准备粘贴到LINQPad):

      Dictionary<string,string> fields1 = new Dictionary<string,string>();
      Dictionary<string,string> fields2 = new Dictionary<string,string>();
      
      fields1.Add("field1", "this");
      fields2.Add("field1", "this");
      fields1.Add("field2", "is");
      fields2.Add("field2", "");
      fields1.Add("field3", "a");
      fields2.Add("field3", null);
      fields1.Add("field4", "test");
      fields2.Add("field4", "test");
      
      var test = 
      from f1 in fields1
          join f2 in fields2
          on f1.Key equals f2.Key
      select (f1.Value ?? "") == (f2.Value ?? "");
      
      test.Dump();
      

      如果您在 2 个索引集合中的字段集以相同的顺序排列,则可以使用以下内容:

      string[] strings1 = { "this", "is", "a", "test" };
      string[] strings2 = { "this", "", null, "test" };
      
      var test = 
      from s1 in strings1.Select((value,index) => new {value, index})
          join s2 in strings2.Select((value,index) => new {value, index})
          on s1.index equals s2.index
      select (s1.value ?? "") == (s2.value ?? "");
      
      test.Dump();
      

      【讨论】:

        【解决方案7】:

        添加几年后,我写了几个相等比较器,我的观点发生了变化,所以我认为相等比较器最好有一个静态成员来保存创建的比较器,而不是每个用户创建一个新实例。


        (原答案,经过上述调整)

        其他人提供的解决方案,包括建议为字符串定义比较类的解决方案,忘记为您的字符串编写新的GetHashCode

        这意味着您的字符串不能在依赖于GetHashCode 的类中使用,例如Dictionary&lt;T&gt;HashSet&lt;T&gt;

        Why is it important to override GetHashCode when Equals method is overridden?

        当您决定为任何类更改平等 的概念时,您应该为该类写一个EqualityComparer。这可以确保如果根据您更改的对象相等定义被认为是相等的,它们的GetHashCode 将返回相等的值。

        public class NullStringComparer : EqualityComparer<string>
        {
            public static IEqualityComparer<string> NullEqualsEmptyComparer {get} = new NullStringComparer();
        
            public override bool Equals(string x, string y)
            {
                // equal if string.Equals(x, y)
                // or both StringIsNullOrEmpty
                return String.Equals(x, y)
                    || (String.IsNullOrEmpty(x) && String.IsNullOrEmpty(y));
            }
        
            public override int GetHashCode(string obj)
            {
                if (String.IsNullOrEmpty(obj))
                   return 0;
                else
                    return obj.GetHashCode();
            }
        }
        

        用法:

        public static void Main()
        {
            string x = null;
            string y = String.Empty;
        
            Console.WriteLine("Standard string comparison: {0}", 
                StringComparer.Ordinal.Equals(x, y));
        
            Console.WriteLine($"My string comparison {0}",
                NullStringComparer.NullEqualsEmpty.Equals(x, y));
        
            // because according to the NullStringComparer x equals y
            // GetHashCode should return the same value
            int hashX = NullStringComparer.NullEqualsEmpty.GetHashCode(x);
            int hashY = NullStringComparer.NullEqualsEmpty.GetHashCode(y);
            Console.WriteLine($"hash X = {hashX}, hash Y = {hashY}");
        } 
        

        【讨论】:

          【解决方案8】:

          这也有效,忽略大小写

          (strA ?? "").Equals(strB ?? "", StringComparison.OrdinalIgnoreCase)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-04-10
            • 1970-01-01
            • 2016-08-10
            • 1970-01-01
            • 1970-01-01
            • 2013-02-10
            • 1970-01-01
            • 2022-11-07
            相关资源
            最近更新 更多