String Сравните, где null и empty равны
Использование С# и .NET 3.5 - лучший способ справиться с этой ситуацией. У меня есть сотни полей для сравнения из разных источников (в основном строки). Иногда источник возвращает поле строки как null, а иногда и пустым. И, конечно, иногда в полях есть текст. Мое текущее сравнение strA!= StrB не разрезает его, потому что strA имеет значение null, а strB - "". Я знаю, что могу сделать string.IsNullOrEmpty, что приводит к двойному сравнению и некоторому уродству. Есть ли лучший способ справиться с этим? Я думал о методах расширения, но вы не можете расширять операторы.
Думаю, я ищу сексуальный способ сделать это.
Ответы
Ответ 1
Поскольку у вас есть сотни сравнений, похоже, что вы хотите, чтобы одна функция вызывала, чтобы вы могли уменьшить беспорядок и повторение в вашем коде. Я не думаю, что есть встроенная функция для выполнения пустой/пустой проверки строк/сравнения в одном, но вы можете просто сделать это самостоятельно:
static class Comparison
{
public static bool AreEqual(string a, string b)
{
if (string.IsNullOrEmpty(a))
{
return string.IsNullOrEmpty(b);
}
else
{
return string.Equals(a, b);
}
}
}
Тогда вы можете просто использовать один вызов вашей функции для каждого сравнения:
if(Comparison.AreEqual(strA[0], strB[0])) { // ... }
if(Comparison.AreEqual(strA[1], strB[1])) { // ... }
if(Comparison.AreEqual(strA[2], strB[2])) { // ... }
if(Comparison.AreEqual(strA[3], strB[3])) { // ... }
Этот подход также легче расширить, если позже вы обнаружите, что вам нужно беспокоиться о дополнительных ситуациях, таких как игнорирование пробелов в начале или конце строк; вы можете просто добавить больше логики к своей функции, чтобы сделать некоторую обрезку или что-то еще, и вам не придется вносить какие-либо изменения в сотни строк кода, вызывающих вашу функцию.
Ответ 2
Не исключает дополнительных базовых сравнений, но для фактора сексуальности вы можете использовать что-то вроде этого:
(strA ?? "") == (strB ?? "")
или немного менее сексуальная, но предпочтительная форма:
(strA ?? string.Empty) == (strB ?? string.Empty)
Ответ 3
Не так сексуально, как, но вы могли бы избежать двойной части сравнения времени, если вы коротко замыкаете ее:
string.IsNullOrEmpty( strA ) ? string.IsNullOrEmpty( strB ) : (strA == strB )
Ответ 4
Что насчет
strA ?? "" == strB ?? ""
Ответ 5
Что случилось с string.IsNullOrEmpty()? Я уверен, что, поскольку он является частью платформы .NET, он оптимизирован и, вероятно, намного эффективнее, чем то, что вы или я могли бы написать. Это может быть не сексуально, но это работает. Напишите код, который легко читается, и пусть компилятор разбирает детали.
Ответ 6
Если ваши 2 набора полей находятся в какой-то коллекции, вы можете использовать LINQ в своих интересах. Если они находятся в какой-то коллекции, которая позволяет вам получить к ним доступ по ключу, и оба они имеют одинаковые ключи, вы можете использовать это (готово для вставки в LINQPad):
Dictionary<string,string> fields1 = new Dictionary<string,string>();
Dictionary<string,string> fields2 = new Dictionary<string,string>();
fields1.Add("field1", "this");
fields2.Add("field1", "this");
fields1.Add("field2", "is");
fields2.Add("field2", "");
fields1.Add("field3", "a");
fields2.Add("field3", null);
fields1.Add("field4", "test");
fields2.Add("field4", "test");
var test =
from f1 in fields1
join f2 in fields2
on f1.Key equals f2.Key
select (f1.Value ?? "") == (f2.Value ?? "");
test.Dump();
Если у вас есть наборы полей из 2 индексированных коллекций в том же порядке, вы можете использовать что-то вроде этого:
string[] strings1 = { "this", "is", "a", "test" };
string[] strings2 = { "this", "", null, "test" };
var test =
from s1 in strings1.Select((value,index) => new {value, index})
join s2 in strings2.Select((value,index) => new {value, index})
on s1.index equals s2.index
select (s1.value ?? "") == (s2.value ?? "");
test.Dump();
Ответ 7
Другие решения, в том числе тот, который предлагает определить класс сравнения для строк, забыл написать новый GetHashCode
для ваших строк.
Это означает, что ваша строка не может использоваться в классах, которые зависят от GetHashCode
как Dictionary<T>
или HashSet<T>
.
См
Почему важно переопределить GetHashCode, когда метод Equals переопределен?
Когда вы решите изменить концепцию равенства для любого класса, вы должны написать EqualityComparer
для этого класса. Это гарантирует, что если в соответствии с вашим измененным определением равенства с объектами считаются равными, их GetHashCode
будет возвращать равные значения.
public class NullStringComparer : EqualityComparer<string>
{
public override bool Equals(string x, string y)
{
// equal if string.Equals(x, y)
// or both StringIsNullOrEmpty
return String.Equals(x, y)
|| (String.IsNullOrEmpty(x) && String.IsNullOrEmpty(y));
}
public override int GetHashCode(string obj)
{
if (String.IsNullOrEmpty(obj))
return 0;
else
return obj.GetHashCode();
}
}
Использование:
public static void Main()
{
string x = null;
string y = String.Empty;
Console.WriteLine($"Standard string comparison: {StringComparer.Ordinal.Equals(x, y)}");
IEqualityComparer<string> equalityComparer = new NullStringComparer();
Console.WriteLine($"My string comparison {equalityComparer.Equals(x, y)}");
int hashX = equalityComparer.GetHashCode(x);
int hashY = equalityComparer.GetHashCode(y);
Console.WriteLine($"hash X = {hashX}, hash Y = {hashY}");
}