В С#, что лучший способ сравнить строки с нулевым и "" return true

У меня есть следующий код (поскольку я пытаюсь обнаружить изменения в поле)

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

проблема заключается в том, что у меня есть случаи, когда person.State имеет значение NULL и source.State is "" и, таким образом, возвращает true.

Если значение null, а другое - пустая строка, я хочу рассматривать их как равные и не обновлять свои данные. Каков самый чистый способ сделать это? Мне нужно создать свой собственный объект Comparer, поскольку это похоже на общую проблему.

Ответ 1

В то время как другие ответы хороши, я бы вытащил их в свой метод, чтобы сделать его более ясным для читателя:

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

Это будет полезно, если вы сравните эти состояния в нескольких местах или разрешите обрабатывать другие нечетные случаи, если они есть. (Примеры могут заключаться в том, чтобы изменить его на регистр, нечувствительный к регистру, или если два состояния являются текстовыми, но одно - аббревиатурой другого, т.е. Вы хотите, чтобы "WI" был равен "Wisconsin".

Ответ 2

Если вам действительно нужно, вы можете:

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; }
    }
}

Ответ 3

Лично я бы дезактивировал/нормализую вверх по течению, но если бы я имел сделать это здесь:

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

Ответ 4

Вы могли бы подумать, что значение enum для 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.

И, как это, вы можете легко добавить варианты сравнения case или culture string...

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

Ответ 5

Мне нужно создать свой собственный объект 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);
  }
}

Ответ 6

Это звучит как идеальное решение для метода расширения.

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

.... или просто используя тело метода расширения, которое я бы предпочел, так как я не вижу в этом слишком большой проблемы стиля.

Ответ 7

Класс String имеет функцию IsNullOrEmpty, которая принимает строку.

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

Из документации:

IsNullOrEmpty - это удобный метод, который позволяет вам одновременно проверяйте, является ли строка нулевой или ее значение Пустое. Это эквивалентен следующему коду:

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, заключается в том, что метод расширения, будучи похожим на метод класса string, фактически преобразован в статический метод со строковой переменной в качестве аргумента ( в соответствии с документацией), поэтому он будет работать, даже если строковая переменная не установлена ​​в экземпляр строки.

Имейте в виду, однако, что выполнение этого способа может привести вас в порядок позже, если вы прочтете код и попытаетесь выяснить, почему он работает, когда person.State или source.State имеет значение null: P

Или, знаешь, иначе я просто напишу полное сравнение:)

Ответ 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);
}

Ответ 9

Ну, пустые и нулевые - это не одно и то же, поэтому вы не имеете в виду общую проблему. Ваша проблема с доменом, когда ваши бизнес-правила требуют, чтобы конкретная оценка была верной. Вы всегда можете написать метод, который выглядит так:

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

Или что-то в этом роде. А потом назовите его повсюду. Вы даже можете сделать это методом расширения.

Ответ 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