【问题标题】:Not nullable types不可为空的类型
【发布时间】:2009-04-05 09:20:20
【问题描述】:

有没有办法在 C# 中创建不可为空的类型(如 DateTime 或 TimeSpan)?

还有一种方法(可能是一个属性)来强制非空参数不会在不添加的情况下传递给方法和属性

if(arg1 == null)
{
   throw new ArgumentNullException("this attribute is null")
}

【问题讨论】:

    标签: c#


    【解决方案1】:

    DateTimeTimeSpan 不可为空,因为它们是 structs 而不是 classes。

    至于你的第二个问题,在 C# 中没有标准的方法可以做到这一点。您可以使用 PostSharp,它是一个 AOP 框架,或者使用 Spec#,这是一种全新的语言(C# 的扩展),它允许一些所需的行为。

    【讨论】:

    • 如果您使用的是构建后步骤,那么绝对有一种标准方法可以做到这一点:代码合同是 .NET 4 的正式组成部分(可单独用于 .NET 3.5)。跨度>
    【解决方案2】:

    在 .NET 4.0 / C# 4.0 中,您所指的空值检查将更容易通过代码合同实现,这几乎可以满足您的需求。

    结构已经是不可为空的,但不要疯狂地创建自己的结构——你很少需要它们(类更常见)。没有“不可为空的类”的真正概念;人们提出了语法更改,例如:

    void Foo(string! arg1) {...}
    

    这将使编译器对arg1 进行非空检查 - 但实际上,代码合同会执行此操作以及更多操作。您可以在 PostSharp 中做一些事情,但可能不值得操之过急。

    关于不可为空的类的另一种想法(以及它们未实现的原因之一); default(T) 对于不可为空的类来说是什么? ;-p 规范要求 default(T) 定义明确...

    【讨论】:

    • 新语法提案和代码契约的区别在于上面可以静态检查。
    • 对于那些正在阅读本文的人,代码契约现在实现了静态检查。
    • string! 在 Spec# 中。
    【解决方案3】:

    一个不可为空的类型是一个ValueType,也就是一个结构体。结构不能为空,因此示例如下:

    public struct MyStruct {}
    

    没有确保 null 不作为参数传递给方法的内置方法(除非参数的类型是 ValueType)。我已经看到人们创建扩展方法来对参数是否为空进行更简单的(即更少的代码)断言,这可能是您的选择。另一方面,检查开始时很短;并且检查的意图非常明确。如果您使用自定义检查方法,情况可能并非如此。

    C# 4.0 将添加更好的选项来通过合同进行此类编程,但目前尚不可用。正如另一个答案中指出的那样,PostSharp 是做你想做的事的一个选择。 PostSharp 通过添加一个添加额外代码的编译后步骤来工作。

    但是,有一些选项可以静态检查是否可以传递 null。例如,ReSharper 允许您使用 [NotNull] 属性装饰自己的方法参数,如果 ReSharper 可以确定参数可能为空,它将在编译时发出警告。当然,这只会警告您(可能)不好的编码实践,它不是运行时检查,不应该这样使用。

    【讨论】:

      【解决方案4】:

      你是对的:与 C++ 相比,这是 C# 的一个缺点。这是一种耻辱,因为我传递给函数的所有参数中有 95% 是非空指针。在 C++ 中,您可以添加经过编译器检查的文档,以指示确保哪些指针指向某物。

      【讨论】:

        【解决方案5】:

        除了提到的 AOP 解决方案之外,Enterprise Library 在其验证块中提供了这一点。 http://msdn.microsoft.com/en-us/library/ff953182(v=pandp.50).aspx

        【讨论】:

          【解决方案6】:

          结构(值类型)变量永远不会为空 - 这解释了您的 DateTime 情况。因此,如果您的方法参数是 C# 结构,您可以确定它们永远不会为空。
          但是,如果您的方法参数是引用类型,它们可以为空。我认为在这种情况下你不能像上面显示的那样取消空检查。

          【讨论】:

            【解决方案7】:

            关于第二个问题,这里有一个灵感来自Nullable<T>

            带有空检查参数的方法如下所示:

            void Foo(NotNull<string> s)
            {
                var x = $"{s}";
                var i = int.Parse(s);
            }
            

            NotNull&lt;T&gt; 的使用不仅限于方法参数。如果该语言将来有一些语法糖,那肯定会很好,例如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");
                    }
                }
            }
            

            【讨论】:

              【解决方案8】:

              当然,您可以编写自己的值类型(enumstruct),它们不能为空(除非设为可空)。

              至于第二部分,您可以有一个通用参数和一个只接受值类型的约束,这意味着参数不能为空 - 考虑到我们使用 class 的绝大多数情况,这不是很有用。

              public static void Do<T>(T arg1) where T : struct
              {
                  //both struct and enum goes here.
              }
              

              【讨论】:

                【解决方案9】:

                根据 Anders Hejlsberg 在 Microsoft Build 2019 中的演讲,C# 8.0 将具有不可为空的类型。 https://mybuild.techcommunity.microsoft.com/sessions/77385?source=sessions

                【讨论】:

                  猜你喜欢
                  • 2010-10-10
                  • 1970-01-01
                  • 2010-10-21
                  • 2020-09-16
                  • 2022-01-22
                  • 2017-01-11
                  • 2011-06-14
                  相关资源
                  最近更新 更多