Если я использую ненулевые ссылочные типы, как мне показать, что я ничего не нашел? - PullRequest
0 голосов
/ 15 мая 2019

Я включил функцию C # 8.0 для необнуляемых ссылочных типов в одном из моих проектов, но сейчас мне неясно, как представлять отсутствующие данные.

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

public (string key, string value) GetKeyValue(string line)
{
    var split = line.Split(':');
    if (split.Length == 2)
        return (split[0].Trim(), split[1].Trim());
    else if (split.Length > 2)
    {
        var joined = string.Join(":", split.ToList().Skip(1));
        return (split[0].Trim(), joined.Trim());
    }
    else
    {
        Debug.Print($"Couldn't parse this into key/value: {line}");
        return (null, null);
    }
}

Что это делает: если у нас есть только одна двоеточие, верните ключ и значение.Если у нас их несколько, объедините остальную часть текста после первого двоеточия, а затем верните ключ и значение.В противном случае у нас нет двоеточий и мы не сможем их проанализировать, поэтому вернем нулевой кортеж.(Давайте предположим, что последний случай может быть разумным; я не могу просто выбросить и назвать его плохим файлом.)

Очевидно, что последняя строка получает предупреждение об обнуляемости, если я не изменю объявление на

public (string? key, string? value) GetKeyValue(string line)

Теперь в F # я бы просто использовал тип Option, а в случае без двоеточий я бы возвратил None.

Но в C # нет типа Option.Я мог бы вернуть ("", ""), но мне кажется, что это не лучше, чем NULL.

В таком случае, какой хороший способ сказать «я ничего не нашел» без использования NULL?

Ответы [ 3 ]

0 голосов
/ 15 мая 2019

На самом деле, теперь я понимаю, что часть проблемы заключается в том, что мой метод делает две разные вещи:

  • Определите, есть ли в строке ключ.
  • Верните ключ иvalue.

Таким образом, возвращаемое значение должно указывать как , есть ли ключ и значение, так и что ключ и значение.

Я могу упростить, выполнив первый элемент отдельно:

bool HasKey(string line)
{
    var split = line.Split(':');
    return split.Length >= 2;
}

Затем в методе, который я написал, если нет ключа, я могу бросить и сказать, что строки должны бытьсначала фильтруется HasKey.

0 голосов
/ 15 мая 2019

Если надеть мою функциональную шапку мышления, тип идиоматического возврата будет IEnumerable<(string?,string?)>.Единственное изменение в вашем коде состоит в том, чтобы изменить return на yield return и удалить оператор return, если ничего не найдено.

public IEnumerable<(string? key, string? value)> GetKeyValue(string line)
{
    var split = line.Split(':');
    if (split.Length == 2)
        return (split[0].Trim(), split[1].Trim());
    else if (split.Length > 2)
    {
        var joined = string.Join(":", split.ToList().Skip(1));
        yield return (split[0].Trim(), joined.Trim());
    }
    else
    {
        Debug.Print($"Couldn't parse this into key/value: {line}");
    }
}

В этом случае вызывающая сторона имеет несколько вариантов обработкиответ.

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

var result = GetKeyValue(line).SingleOrDefault();
if (!result.HasValue) HandleKeyNotFound();

Если они предпочитают генерировать исключение, если ключ не найден, они сделали бы это:

var result = GetKeyValue(line).Single();

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

foreach (var result in GetKeyValue(line)) DoSomething(result.Item1, result.Item2);

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

0 голосов
/ 15 мая 2019

Вы можете включить, если результат был успешным при разборе, просто вернув флаг:

public class Result
{
    private Result(){}

    public bool Successful {get;private set;} = false;

    public string Key {get; private set;} = string.Empty;

    public string Value {get; private set;} = string.Empty;

    public static Successful(string key, string value)
    {
        return new Result
        {
            Successful = true,
            Key = key,
            Value = value
        };
    }

    public static Failed()
    {
        return new Result();
    }
}

public Result GetKeyValue(string line){
     return Result.Failed();
}

Тогда вы можете использовать его как

var result = GetKeyValue("yoda");

if(result.Successful)
{
    // do something...
}

В качестве альтернативы вы можете вернуть 2 различных типаи использовать сопоставление с образцом ?

...