【问题标题】:In C#, what is the best way to compare strings with null and "" return true在 C# 中,将字符串与 null 和“”返回 true 进行比较的最佳方法是什么
【发布时间】:2012-08-13 07:14:25
【问题描述】:

我有以下代码(因为我正在尝试检测字段的更改)

 if (person.State != source.State)
 {
      //update my data . .
  }

问题是我遇到 person.State 为 NULL 且 source.State 为 "" 并因此返回 true 的情况。

如果一个为空,另一个为空字符串,我想将它们视为相等并且不更新我的数据。最干净的方法是什么?我是否需要创建自己的 Comparer 对象,因为这似乎是一个普遍的问题

【问题讨论】:

  • 顺便说一句,Microsoft.VisualBasic 中是否有一个公共字符串比较例程可以实现这一点,因为这是 VB.NET 字符串的默认比较?

标签: c# string string-comparison


【解决方案1】:

如果你真的需要,你可以这样做:

if ((person.State ?? string.Empty) != (source.State ?? string.Empty))
{
    // ...
}

但是,根据您的需要,更好的解决方案可能是修改您的 person.State 属性以从不返回空值。

public class Person
{
    string _state = string.Empty;
    public string State
    {
        get { return _state; }
        set { _state = value ?? string.Empty; }
    }
}

【讨论】:

  • 一个非常小的观察结果(与问题无关),但""string.Empty 一样有效,更易于阅读,并且不会被反射滥用...@987654327 @
  • @MarcGravell StyleCop recommends string.Empty instead of ""。我不确定支持或反对的原因。
  • @MarcGravell 老实说,我发现string.Emtpy 更易于阅读。有一点旁观者的眼睛。
【解决方案2】:

就我个人而言,我会清理/规范化上游,但如果我不得不在这里这样做:

// check different, treating null & "" as equivalent
if ((person.State ?? "") != (source.State ?? ""))

【讨论】:

  • 正打算发布同样的内容。最好在前端强制执行此要求,而不是在后端修改您的代码。当然,有时存在使这种事情不切实际的情况,但通常情况下是可能的并且不会过于困难。 +1
【解决方案3】:

虽然其他答案很好,但我会将它们提取到自己的方法中,以使读者更清楚:

public static bool StatesEqual(string first, string second)
{
  return first ?? "" == second ?? "";
}

如果您在多个地方比较这些状态,或者允许您处理其他奇怪的情况(如果有),这将是有益的。 (示例可能是将其更改为不区分大小写,或者如果两个状态在文本上不同但一个是另一个的缩写,即您希望“WI”等于“Wisconsin”。

【讨论】:

  • 有关更多信息:“==”本质上是 x.Equals(y),因此它执行“序数(区分大小写和不区分区域性)比较。” (MSDN)
  • 是什么使它成为一个好的解决方案:+1:“==”是最简洁的样式(如果可能的话)。 +1:如果你想让“null==null”为真,这是合乎逻辑的,但与“==”和等于的 C# 标准定义相反。 +1:序数和不区分大小写可能(?)是大多数初学者所期望的,也是专家在这里应该知道的标准。
  • 当这种方法通常不太合适时:-1:您不想破坏标准的“等于”行为“null 等于无”。 -1:您更喜欢详细说明您当前使用的文化和大小写(此处:StringComparison.Ordinal)。 -1:您需要特殊行为,尤其是不敏感的大小写(StringComparison.OrdinalIgnoreCase)或其他东西。 -1:您不想通过手动处理 null 值来使代码混乱(IMO:这里是这样的正确位置,但是:尝试通过其他方式在任何地方避免这样的条件,以确保在此之前这些值不能是空。)
【解决方案4】:

你会认为会有一个 StringComparison 枚举值来用 String.Equals... 处理这个问题,或者有一个 CompareOptions 枚举值来用 String.Compare... 处理它,但没有。

无论如何,我认为您仍然应该使用 String.Equals 作为最佳实践。

string s1 = null;
string s2 = string.Empty;

bool areEqual = string.Equals(s1 ?? string.Empty, s2 ?? string.Empty);

// areEqual is now true.

这样,您可以轻松添加大小写或文化字符串比较选项...

bool areEqual = string.Equals(s1 ?? string.Empty, s2 ?? string.Empty, StringComparison.OrdinalIgnoreCase);

【讨论】:

    【解决方案5】:

    这听起来像是扩展方法的完美解决方案。

        public static bool IsEqualNoNulls(this String str, string cmp) //bad name, but you get the point
        {
            return (str ?? "") == (cmp ?? "");
        }
    

    .... 或者只是使用我可能更喜欢的扩展方法的主体,因为我不认为这是一个太大的风格问题。

    【讨论】:

      【解决方案6】:

      String 类有一个函数“IsNullOrEmpty”,它接受一个字符串。

      http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.aspx

      来自文档:

      IsNullOrEmpty 是一种方便的方法,可让您 同时测试一个 String 是否为 null 或其值为 Empty。它 相当于下面的代码:

      result = s == null || s == String.Empty;
      

      例如:

      if (!(string.IsNullOrEmpty(person.State) && string.IsNullOrEmpty(source.State)))
      {
            //update your data . .
      }
      

      或者您可以使用扩展方法,类似于@Earlz 概述的方法

      您可以在此处了解更多信息http://msdn.microsoft.com/en-us/library/bb383977.aspx

      因此,假设我有如下扩展方法:

      public static bool IsBlank(this string str)
      {
          return string.IsNullOrEmpty(str);
      }
      

      这将允许您执行类似的操作

      if(!(person.State.IsBlank() && source.State.IsBlank())
      {
           //do something
      }
      

      即使 person.State 或 source.State 为 null 也能正常工作的原因是扩展方法虽然看起来像字符串类的方法,但实际上转换为静态方法字符串变量作为它的参数(根据文档),所以即使字符串变量没有设置为字符串的实例,它也会很高兴地工作。

      但请记住,如果您正在阅读代码并试图弄清楚当 person.State 或 source.State 设置为 null 时它为什么会起作用,那么这样做可能会在以后绊倒您:

      或者,你知道,或者我只是写一个完整的比较:)

      【讨论】:

      • 您现在不再比较这两个值
      • 啊,一个很好的观点。我想我忽略了代码的最初目的
      【解决方案7】:

      我是否需要创建自己的 Comparer 对象,因为这似乎是一个普遍的问题

      现在从这里的好答案应该很清楚,你没有,但是如果你一遍又一遍地做这种比较,或者想使用状态作为键,那么:

      public class NullEmptStringComparer : IComparer<string>
      {
        public Equals(string x, string y)
        {
          return (x ?? string.Empty) == (y ?? string.Empty);
        }
        public int GetHashCode(string str)
        {
          return (str ?? string.Empty).GetHashCode();
        }
      }
      

      或者基于另一个比较,以防默认的 == 比较不合适(实际上很少这样):

      public class NullEmptCustStringComparer : IComparer<string>
      {
        private readonly IComparer<string> _baseCmp;
        public NullEmptCustStringComparer(IComparer<string> baseCmp)
        {
          _baseCmp = baseCmp;
        }
        public Equals(string x, string y)
        {
          return _baseCmp.Equals(x ?? string.Empty, y ?? string.Empty);
        }
        public int GetHashCode(string str)
        {
          return _baseCmp.GetHashCode(str ?? string.Empty);
        }
      }
      

      【讨论】:

        【解决方案8】:

        所有给出的答案都将失败Turkey Test。试试这个:

        public static bool StatesEqual(string first, string second)
        {
            if (first == null || second == null)
                return false; // You can also use return first == second if you want to compare null values.
        
            return first.Equals(second, StringComparison.InvariantCulture);
        }
        

        【讨论】:

        • 我喜欢这个,但它是要说明如果一个人更喜欢二进制比较(例如更快)并且想要忽略语言专业,那么 StringComparison.OrdinalIgnoreCase 更合适。或者正如 MSDN“在 .NET Framework 中使用字符串的最佳实践”中所建议的那样,仅当比较与语言相关时才使用 InvariantCulture。没有好坏之分,这取决于您的喜好。
        • 一个例子,我经常比较德语“文化”语言环境的文本,但特别是尝试考虑德语和类似语言专业的 InvariantCulture 的行为不适合我:InvariantCulture认为“ss”等于“ß”,这在大多数情况下不是我想要的,因为它在德语中绝对不一样,而且我认为这绝对是“InvariantCulture”作为“InvariantCulture”的错误选择(英语语言学家)母语人士,但适合“InvariantCultureIgnoreCase”。
        • 查看这里的 InvariantCulture 和 Ordinal:stackoverflow.com/questions/492799/…
        • 还有一条评论:土耳其测试主要是相关的,如果你想处理比较不区分大小写或 ToUpper()/ToLower() 的东西。所以“==”就足够了,而且是一个很好的区分大小写的比较风格。
        【解决方案9】:

        好吧,empty 和 null 不是一回事,所以你在这里指的不是一般问题。你的问题是一个领域问题,你的业务规则要求特定的评估是真实的。您总是可以编写如下所示的方法:

        public static bool AreMyStringsCustomEqual(string s1, string s2) {
            return (s1 == null || s1 == "" && s2 == null || s2 == "");
        }
        

        或者类似的东西。然后从任何地方调用它。您甚至可以将其作为扩展方法。

        【讨论】:

        • 这无法对除 null 或 "" 以外的任何值应用整体相等条件;例如,“abc”和“abc”(错误)返回false,null和“abc”(错误)返回true,“abc”和“”(错误)返回true
        【解决方案10】:

        我相信这是 Decorator 模式的一个例子。你需要装饰一个股票 StringComparer 来做你想做的事:

        public enum Collapse
        {
          None                      = 0 ,
          EmptyAndWhitespace        = 1 ,
          NullAndWhitespace         = 2 ,
          NullAndEmpty              = 3 ,
          NullAndEmptyAndWhitespace = 4 ,
        }
        
        public class MySpecialStringComparerDecorator : StringComparer
        {
          const   string         COLLAPSED_VALUE = "" ;
          private StringComparer instance ;
          private Collapse     rule     ;
        
          public StringComparer Decorate( StringComparer sc , Collapse equivalencyRule )
          {
            StringComparer instance = new MySpecialStringComparer( sc , equivalencyRule ) ;
            return instance ;
          }
        
          private MySpecialStringComparerDecorator( StringComparer comparer , Collapse equivalencyRule )
          {
            if ( comparer == null                                  ) throw new ArgumentNullException("comparer") ;
            if ( !Enum.IsDefined(typeof(Collapse),equivalencyRule) ) throw new ArgumentOutOfRangeException("equivalencyRule") ;
        
            this.instance = comparer ;
            this.rule     = equivalencyRule ;
        
            return ;
          }
        
          private string CollapseAccordingToRule( string s )
            {
                string collapsed = s ;
                if ( rule != Collapse.None )
                {
                    if ( string.IsNullOrWhiteSpace(s) )
                    {
                        bool isNull  = ( s == null ? true : false ) ;
                        bool isEmpty = ( s == ""   ? true : false ) ;
                        bool isWS    = !isNull && !isEmpty ;
        
                        switch ( rule )
                        {
                            case Collapse.EmptyAndWhitespace        : if ( isNull||isWS          ) collapsed = COLLAPSED_VALUE ; break ;
                            case Collapse.NullAndEmpty              : if ( isNull||isEmpty       ) collapsed = COLLAPSED_VALUE ; break ;
                            case Collapse.NullAndEmptyAndWhitespace : if ( isNull||isEmpty||isWS ) collapsed = COLLAPSED_VALUE ; break ;
                            case Collapse.NullAndWhitespace         : if ( isNull||isWS          ) collapsed = COLLAPSED_VALUE ; break ;
                            default                                 : throw new InvalidOperationException() ;
                        }
                    }
                }
                return collapsed ;
            }
        
          public override int Compare( string x , string y )
          {
            string a     = CollapseAccordingToRule(x) ;
            string b     = CollapseAccordingToRule(y) ;
            int    value = instance.Compare(a,b);
            return value ;
          }
        
          public override bool Equals( string x , string y )
          {
            string a     = CollapseAccordingToRule(x) ;
            string b     = CollapseAccordingToRule(y) ;
            bool   value = instance.Equals(a,b) ;
            return value ;
          }
        
          public override int GetHashCode( string obj )
          {
            string s     = CollapseAccordingToRule(obj) ;
            int    value = instance.GetHashCode( s ) ;
            return value ;
          }
        
        }
        

        用法很简单:

        StringComparer sc = new MySpecialStringDecorator( StringComparer.CurrentCultureIgnoreCase , Collapse.NullAndEmptyAndWhitespace ) ;
        
        // go to town
        

        【讨论】:

        • 看起来像是过度设计的完美例子
        • “过度设计”将涉及一个工厂类,该类将根据文化返回一个经过适当装饰的StringComparer —当前的、不变的、有序的或特定的——区分大小写和等效规则,每个等效规则的装饰器都有一个单独的子类型。 :D
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-03
        • 1970-01-01
        • 2019-04-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多