13

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

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

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

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

4

10 回答 10

22

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

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; }
    }
}
于 2012-08-16T21:24:44.233 回答
14

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

// check different, treating null & "" as equivalent
if ((person.State ?? "") != (source.State ?? ""))
于 2012-08-16T21:23:59.963 回答
12

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

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

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

于 2012-08-16T21:30:04.800 回答
3

你会认为会有一个 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);
于 2012-08-16T21:50:16.593 回答
2

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

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

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

于 2012-08-17T00:54:08.077 回答
2

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 时它为什么起作用,那么这样做可能会在以后绊倒您:P

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

于 2012-08-17T03:40:28.007 回答
1

我是否需要创建自己的 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);
  }
}
于 2012-08-16T21:46:55.773 回答
1

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

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);
}
于 2012-08-22T14:01:26.040 回答
0

我相信这是装饰器模式的一个案例。你需要装饰一个股票 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
于 2012-08-16T23:53:47.883 回答
0

Well, empty and null are not the same thing, so you're not referring to a generic problem here. Yours is a domain problem where your business rules demand that particular evaluation as being true. You can always write a method that looks like this:

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

Or something like that. And then call it from everywhere. You could even make it an extension method.

于 2012-08-16T21:28:26.937 回答