在C#中,将字符串与null,””进行比较返回true的最佳方法是什么

In C#, what is the best way to compare strings with null and “” return true

我有以下代码(当我试图检测字段的更改时)

1
2
3
4
 if (person.State != source.State)
 {
      //update my data . .
  }

问题是我遇到了这样的情况:person.state为空,source.state为"",因此返回true。

如果一个为空,另一个为空字符串,我希望将它们视为相等,而不更新我的数据。最干净的方法是什么?是否需要创建自己的比较器对象,因为这似乎是一个常规问题


如果你真的需要,你可以:

1
2
3
4
if ((person.State ?? string.Empty) != (source.State ?? string.Empty))
{
    // ...
}

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

1
2
3
4
5
6
7
8
9
public class Person
{
    string _state = string.Empty;
    public string State
    {
        get { return _state; }
        set { _state = value ?? string.Empty; }
    }
}


就我个人而言,我会对上游进行消毒/正常化,但如果我必须在这里这样做:

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


虽然其他答案都是好的,但我会把它们放到自己的方法中,让读者更清楚地看到:

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

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


您会认为有一个StringComparison枚举值可以用String.Equals处理这个问题…或CompareOptions枚举值以用字符串处理它。比较…但没有。

无论如何,我认为你还是应该使用string.equals作为最佳实践。

1
2
3
4
5
6
string s1 = null;
string s2 = string.Empty;

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

// areEqual is now true.

像这样,您可以轻松地添加case或culture字符串比较选项…

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

所有给出的答案都将通过土耳其考试。试试这个:

1
2
3
4
5
6
7
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);
}


string类有一个函数"isNullOrEmpty",它接受一个字符串。

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

从文档中:

IsNullOrEmpty is a convenience method that enables you to
simultaneously test whether a String is null or its value is Empty. It
is equivalent to the following code:

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

例如:

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

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

您可以在http://msdn.microsoft.com/en-us/library/bb383977.aspx了解更多有关它们的信息。

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

1
2
3
4
public static bool IsBlank(this string str)
{
    return string.IsNullOrEmpty(str);
}

这样你就可以做

1
2
3
4
if(!(person.State.IsBlank() && source.State.IsBlank())
{
     //do something
}

即使person.state或source.state为空,这种方法也能正常工作的原因是,扩展方法虽然看起来像字符串类的方法,但实际上已转换为静态方法,并将字符串变量作为其参数(根据文档),因此即使字符串变量未设置为字符串的实例,它也能正常工作。

不过,请记住,如果您正在读取代码,并试图找出当person.state或source.state设置为空时它为什么工作,那么这样做可能会在以后的某个时间绊倒您:p

或者,你知道,我也可以写一个完整的比较。


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

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

…或者只是使用扩展方法的主体,我可能更喜欢,因为我不认为这是样式问题。


Do i need to create my own Comparer object as this seems like a generic problem

现在,从这里的好答案中应该可以清楚地看到,您没有,但是如果您反复进行这种比较,或者希望使用状态作为键,那么:

1
2
3
4
5
6
7
8
9
10
11
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();
  }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
  }
}

我相信这是一个装饰图案的例子。你需要装饰一个股票比较器来做你想做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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 ;
  }

}

用法很简单:

1
2
3
StringComparer sc = new MySpecialStringDecorator( StringComparer.CurrentCultureIgnoreCase , Collapse.NullAndEmptyAndWhitespace ) ;

// go to town


好吧,空值和空值不是同一回事,所以这里并不是指一般性问题。您的领域问题是,您的业务规则要求特定的评估是真实的。您始终可以编写如下所示的方法:

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

或者类似的。然后到处叫它。甚至可以将其作为扩展方法。