我知道C#中的字符串可以通过使用null来“可空”。
但是,可空类型的全部意义在于我从编译器获得帮助[编辑:又名,输入错误:不能将苹果添加到香蕉中]
使用这种'类型系统hack-ish'的'可空性取决于基础类型'打破了我可能拥有的任何保证(这不是一个小小的壮举,因为这似乎是在第一个使用类型系统的全部要点地方..)
如果有人愿意,在C#中处理此问题的标准方法是什么? 我要滚动自己的“可空”课吗?
修改
让我重新解释一下这个问题:
在C#中,标准方法是确保您注释为可空的变量不会被分配给您未注释为可为空的变量。
那是: 什么是适用于所有类型的标准方法,正是?关键字为您提供的值类型。
答案 0 :(得分: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>
。
这种方法的问题是:
它不起作用。虽然该方法的返回值保证不是null
,但SurelyNotNull<T>.Value
的返回值不是struct
。乍一看,它似乎保证不为空。但它可以是,因为这个:
每个结构都有一个隐式的,公共的,无参数的构造函数,即使定义了另一个构造函数
以下代码有效,并使用上面的new SurelyNotNull<string>();
进行编译:
Value
<强>结论:强>
在C#中,你无法实现你想要做的事情
您仍然可以使用此方法,您只需要了解某人可以使用此类型并仍然生成空值。要在这种情况下快速失败,最好在_value
的getter中添加一个检查,并在{{1}}为空时抛出异常。
答案 1 :(得分: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的客户反馈网站上对此问题进行投票:Add non-nullable reference types in C#
答案 2 :(得分:2)
你想要一个类似NotNull属性的东西。使用这些将为您提供编译时警告和错误,并在某些情况下提供有关为NotNull属性分配NULL值的IDE反馈。
请参阅此问题:C#: How to Implement and use a NotNull and CanBeNull attribute
答案 3 :(得分:0)
所有reference types都可以为空。 String是引用类型。