如果我使用的是非空引用类型,如何显示我什么都没找到?

时间:2019-05-15 08:48:06

标签: c# nullable non-nullable

我已经在我的一个项目中启用了C# 8.0 non-nullable reference types功能,但是现在我不清楚如何表示丢失的数据。

例如,我正在读取一个文件,其行是用冒号分隔的键/值对。有时,一行上有多个冒号。在这种情况下,第一个冒号之前的文本是键,其余部分是值。我解析每一行的代码如下:

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 :(得分:0)

您可以通过仅返回一个标志来包括结果是否解析成功:

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种不同的类型并使用模式匹配

答案 1 :(得分:0)

实际上,我现在意识到问题的一部分在于我的方法正在做两件事:

  • 确定该行是否有键。
  • 返回键和值。

因此返回值必须同时表明是否和键,以及什么键和值。

我可以通过单独做第一项来简化:

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

然后在我发布的方法中,如果没有键,我可以抛出并说需要首先由HasKey过滤行。

答案 2 :(得分:0)

放下我的功能性思维上限,惯用的返回类型为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}");
    }
}

然后,呼叫者可以选择几种方法来处理响应。

如果他们想检查密钥是否是老式的eway,请执行以下操作:

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而不是元组,因为它可以明确传达字段的目的。