1

我知道 C# 中的字符串可以通过使用 null 来“为空”。

但是,可空类型的全部意义在于我从编译器那里获得了帮助[编辑:又名,类型错误:无法将苹果添加到香蕉中]

并且使用这种“可空性取决于底层类型”的“类型系统hack-ish”打破了我可能拥有的任何保证(这不是一个小壮举,因为这似乎是首先使用类型系统的全部意义...... )

如果有人愿意,在 C# 中处理这个问题的标准方法是什么?我应该推出自己的“可空”类吗?

编辑

让我重新表述一下这个问题:

在 C# 中,确保您注释为可为空的变量不会被分配给您未注释为可为空的变量的标准方法是什么。

那就是:所有类型的标准方法是什么,究竟是什么关键字为您提供值类型。

4

4 回答 4

4

如果您要询问一种方法来确保某个方法的返回类型不是null,那么有一个解决方案:该方法必须返回一个值类型。每个返回引用类型的方法都可能返回null. 您对此无能为力。

因此,您可以创建一个struct这样的函数来返回值类型:

public struct SurelyNotNull<T>
{
    private readonly T _value;

    public SurelyNotNull(T value)
    {
        if(value == null)
            throw new ArgumentNullException("value");
        _value = value;
    }

    public T Value
    {
        get { return _value; }
    }
}

您应该返回字符串的方法现在可以返回SurelyNotNull<string>

这种方法的问题是:
它不起作用。虽然方法的返回值保证为 not null,但 的返回值SurelyNotNull<T>.Value不是。乍一看,它似乎保证不为空。但它可能是这样的:
每个结构都有一个隐式的、公共的、无参数的构造函数,即使定义了另一个构造函数
以下代码是有效的,并与struct上面的代码一起编译:

new SurelyNotNull<string>();

结论:
在 C# 中,您无法实现您想要做的事情。
您仍然可以使用这种方法,您只需要了解有人可以使用这种类型并仍然产生空值。要在这种情况下快速失败,最好在 getter 中添加检查,如果为 null Value,则抛出异常。_value

于 2013-02-19T17:21:35.143 回答
2

正如 Daniel Hilgarth 指出的那样,没有万无一失的方法可以实现这一目标。我的建议与他的类似,但增加了一些安全性。您可以自己决定在整个程序中使用包装器类型的好处是否超过成本。

struct NonNull<T> where T : class {
    private readonly T _value;
    private readonly bool _isSafe;

    public NonNull(T value) {
        if (value == null)
            throw new ArgumentNullException();
        _value = value;
        _isSafe = true;
    }
    public T Value {
        get {
            if (_isSafe) return _value;
            throw new ArgumentNullException();
        }
    }
    public static implicit operator T(NonNull<T> nonNull) {
        return nonNull.Value;
    }
}

static class NonNull {
    public static NonNull<T> Create<T>(T value) where T : class {
        return new NonNull<T>(value);
    }
}

包装器类型主要是为了使您的意图自我记录,因此您不太可能使用零初始化结构绕过它,但它保留一个标志以指示它无论如何都已正确初始化。在那种公认的不寻常的情况下,它会ArgumentNullException在访问该值时抛出一个。

class Program {
    static void Main(string[] args) {
        IsEmptyString(NonNull.Create("abc")); //false
        IsEmptyString(NonNull.Create("")); //true
        IsEmptyString(null); //won't compile
        IsEmptyString(NonNull.Create<string>(null)); //ArgumentNullException 
        IsEmptyString(new NonNull<string>()); //bypassing, still ArgumentNullException
    }

    static bool IsEmptyString(NonNull<string> s) {
        return StringComparer.Ordinal.Equals(s, "");
    }
}

现在,这比偶尔的 NRE 更好吗?也许。它可以节省大量样板 arg 检查。您需要确定它是否适合您的情况。缺少编译器支持,就像 F# 提供的那样,没有办法提供编译时空安全性,但您可以(可以说)简化运行时安全性。

您可能希望在 Microsoft 的客户反馈网站上对此问题进行投票:在 C# 中添加不可为空的引用类型

于 2013-02-19T17:38:06.800 回答
2

您想要类似 NotNull 属性的东西。使用这些会给你编译时警告和错误,并且在某些情况下 IDE 反馈关于将 NULL 值分配给 NotNull 属性。

看到这个问题:C#:如何实现和使用 NotNull 和 CanBeNull 属性

于 2013-02-19T18:02:18.287 回答
0

所有引用类型都可以为空。字符串是引用类型。

于 2013-02-19T16:26:58.687 回答