【发布时间】:2009-04-05 09:20:20
【问题描述】:
有没有办法在 C# 中创建不可为空的类型(如 DateTime 或 TimeSpan)?
还有一种方法(可能是一个属性)来强制非空参数不会在不添加的情况下传递给方法和属性
if(arg1 == null)
{
throw new ArgumentNullException("this attribute is null")
}
【问题讨论】:
标签: c#
有没有办法在 C# 中创建不可为空的类型(如 DateTime 或 TimeSpan)?
还有一种方法(可能是一个属性)来强制非空参数不会在不添加的情况下传递给方法和属性
if(arg1 == null)
{
throw new ArgumentNullException("this attribute is null")
}
【问题讨论】:
标签: c#
DateTime 和 TimeSpan 不可为空,因为它们是 structs 而不是 classes。
至于你的第二个问题,在 C# 中没有标准的方法可以做到这一点。您可以使用 PostSharp,它是一个 AOP 框架,或者使用 Spec#,这是一种全新的语言(C# 的扩展),它允许一些所需的行为。
【讨论】:
在 .NET 4.0 / C# 4.0 中,您所指的空值检查将更容易通过代码合同实现,这几乎可以满足您的需求。
结构已经是不可为空的,但不要疯狂地创建自己的结构——你很少需要它们(类远更常见)。没有“不可为空的类”的真正概念;人们提出了语法更改,例如:
void Foo(string! arg1) {...}
这将使编译器对arg1 进行非空检查 - 但实际上,代码合同会执行此操作以及更多操作。您可以在 PostSharp 中做一些事情,但可能不值得操之过急。
关于不可为空的类的另一种想法(以及它们未实现的原因之一); default(T) 对于不可为空的类来说是什么? ;-p 规范要求 default(T) 定义明确...
【讨论】:
string! 在 Spec# 中。
一个不可为空的类型是一个ValueType,也就是一个结构体。结构不能为空,因此示例如下:
public struct MyStruct {}
没有确保 null 不作为参数传递给方法的内置方法(除非参数的类型是 ValueType)。我已经看到人们创建扩展方法来对参数是否为空进行更简单的(即更少的代码)断言,这可能是您的选择。另一方面,检查开始时很短;并且检查的意图非常明确。如果您使用自定义检查方法,情况可能并非如此。
C# 4.0 将添加更好的选项来通过合同进行此类编程,但目前尚不可用。正如另一个答案中指出的那样,PostSharp 是做你想做的事的一个选择。 PostSharp 通过添加一个添加额外代码的编译后步骤来工作。
但是,有一些选项可以静态检查是否可以传递 null。例如,ReSharper 允许您使用 [NotNull] 属性装饰自己的方法参数,如果 ReSharper 可以确定参数可能为空,它将在编译时发出警告。当然,这只会警告您(可能)不好的编码实践,它不是运行时检查,不应该这样使用。
【讨论】:
你是对的:与 C++ 相比,这是 C# 的一个缺点。这是一种耻辱,因为我传递给函数的所有参数中有 95% 是非空指针。在 C++ 中,您可以添加经过编译器检查的文档,以指示确保哪些指针指向某物。
【讨论】:
除了提到的 AOP 解决方案之外,Enterprise Library 在其验证块中提供了这一点。 http://msdn.microsoft.com/en-us/library/ff953182(v=pandp.50).aspx
【讨论】:
结构(值类型)变量永远不会为空 - 这解释了您的 DateTime 情况。因此,如果您的方法参数是 C# 结构,您可以确定它们永远不会为空。
但是,如果您的方法参数是引用类型,它们可以为空。我认为在这种情况下你不能像上面显示的那样取消空检查。
【讨论】:
关于第二个问题,这里有一个灵感来自Nullable<T>。
带有空检查参数的方法如下所示:
void Foo(NotNull<string> s)
{
var x = $"{s}";
var i = int.Parse(s);
}
NotNull<T> 的使用不仅限于方法参数。如果该语言将来有一些语法糖,那肯定会很好,例如Foo(string! s).
public struct NotNull<T> where T : class
{
private T valueField;
public NotNull(T value)
{
this.valueField = value;
this.CheckNotNull(value);
}
public T Value => this.valueField;
public static implicit operator T(NotNull<T> t)
{
return t.Value;
}
public static implicit operator NotNull<T>(T t)
{
return new NotNull<T>(t);
}
public override bool Equals(object other)
{
return this.Value.Equals(other);
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
public override string ToString()
{
return this.Value.ToString();
}
private void CheckNotNull(T value)
{
if (value == null)
{
throw new InvalidOperationException($"Value cannot be null");
}
}
}
【讨论】:
当然,您可以编写自己的值类型(enum 和 struct),它们不能为空(除非设为可空)。
至于第二部分,您可以有一个通用参数和一个只接受值类型的约束,这意味着参数不能为空 - 考虑到我们使用 class 的绝大多数情况,这不是很有用。
public static void Do<T>(T arg1) where T : struct
{
//both struct and enum goes here.
}
【讨论】:
根据 Anders Hejlsberg 在 Microsoft Build 2019 中的演讲,C# 8.0 将具有不可为空的类型。 https://mybuild.techcommunity.microsoft.com/sessions/77385?source=sessions
【讨论】: