В C#, каков наилучший способ сравнить строки с null и "" вернуть true

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

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

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

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

10 ответов


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

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

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


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

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

однако, в зависимости от ваших потребностей, лучшим решением может быть изменение вашего person.State свойство никогда не возвращать значения null.

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

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

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

вы думаете, что будет значение перечисления StringComparison для обработки этого со строкой.Равняется... или CompareOptions значения enum, чтобы справиться с строку.Сравнивать... но нет.

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

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

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

// areEqual is now true.

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

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

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

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

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

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


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

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

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

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

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
}

причина, по которой это работает, даже если человек.Государство или источник.Состояние null потому что метод расширения, выглядящий как метод класса string, фактически преобразуется в статический метод со строковой переменной в качестве аргумента (согласно документации), поэтому он будет счастливо работать, даже если строковая переменная не установлена в экземпляр string.

имейте в виду, однако, что это может сбить вас с толку позже, если Вы читаете код и пытаюсь выяснить, почему это работает, когда человек.Государство или источник.Состояние имеет значение null: P

или, знаете, в качестве альтернативы я бы просто написал сравнение в полном объеме:)


все данные ответы не будут выполнены Турция Тест. Попробуйте вместо этого:

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

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

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

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


Я считаю, что это дело для оформителя узор. Вам нужно украсить запас 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