Подтвердить что ты не робот

String Сравните, где null и empty равны

Использование С# и .NET 3.5 - лучший способ справиться с этой ситуацией. У меня есть сотни полей для сравнения из разных источников (в основном строки). Иногда источник возвращает поле строки как null, а иногда и пустым. И, конечно, иногда в полях есть текст. Мое текущее сравнение strA!= StrB не разрезает его, потому что strA имеет значение null, а strB - "". Я знаю, что могу сделать string.IsNullOrEmpty, что приводит к двойному сравнению и некоторому уродству. Есть ли лучший способ справиться с этим? Я думал о методах расширения, но вы не можете расширять операторы.

Думаю, я ищу сексуальный способ сделать это.

4b9b3361

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