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)) { // ... } |
但是,根据需要,更好的解决方案可能是修改
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 ==""); } |
或者类似的。然后到处叫它。甚至可以将其作为扩展方法。