在C#/。NET中将参数标记为不能为空吗?


98

有没有我可以分配给防止null在C#/。NET中传递的函数参数的简单属性或数据协定?理想情况下,这还将在编译时进行检查,以确保在null任何地方和运行时throw都不会使用文字ArgumentNullException

目前我写类似...

if (null == arg)
  throw new ArgumentNullException("arg");

...对于我希望不会出现的每个论点null

在同一点上,是否存在与Nullable<>下列情况相反的情况:

NonNullable<string> s = null; // throw some kind of exception

7
在最新版本的C#中,使用“ nameof()”效果更好:抛出新的ArgumentNullException(nameof(arg)); 这样,如果您重构名称,并且在您的throw语句中
起伏不定

C#8现在支持可为空的引用类型,这是可以在项目中打开的编译器选项。docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Paul Stegler

Answers:


69

不幸的是,在编译时没有可用的东西。

我最近在我的博客上发布了一个有问题的解决方案,该解决方案使用了新的结构和转换。

在带有Code Contracts的 .NET 4.0中,生活会好很多。具有非空性的实际语言语法和支持仍然会很不错,但是代码协定会有所帮助。

我在MiscUtil中还有一个名为ThrowIfNull 的扩展方法,这使它变得更简单了。

最后一点-使用“ if (null == arg)”代替“ if (arg == null)”的任何理由?我发现后者更容易阅读,并且前者在C中解决的问题不适用于C#。


2
>不幸的是,在编译时没有可用的东西。> ...>我同意,拥有真正的语言语法和围绕非空性的支持仍然非常不错。我还希望看到出现编译时错误。
2009年

2
@Jon,如果碰巧存在对bool定义的隐式强制转换,“ if(arg = null)”不起作用吗?我承认它看起来似乎是错误的,但是它确实可以编译...
Thomas S. Trias

11
@ ThomasS.Trias:是的,在那种难以置信的边缘情况下,加上拼写错误,再加上围绕该代码的缺乏测试,您最终会遇到问题。到那时,我认为您还有更大的问题:)
Jon Skeet

4
@乔恩:被授予。我想我会放弃我心爱的Yoda条件。:-)
Thomas S. Trias

1
@SebastianMach:我不相信这if (null == x)是由于自然语言差异引起的。我相信它的的确确是一个过时的风格,通过实例等只是传播
乔恩斯基特

21

我知道我对这个问题迟到了,但是我认为答案会变得有意义,因为C#的最新主要版本即将发布,然后发布。在C#8.0中,将发生重大变化,C#将假定所有类型都被视为非null。

根据Mads Torgersen的说法:

问题在于空引用是如此有用。在C#中,它们是每种引用类型的默认值。默认值是什么?变量可以具有什么其他值,直到您可以确定要分配给它的其他值?在您开始填写参考分配的数组之前,我们还能为其分配其他什么值?

同样,有时null本身就是一个明智的价值。有时您想表示一个事实,例如,一个字段没有值。可以为参数传递“无”。不过,有时还是要重点关注。这又是问题的另一部分:C#之类的语言不允许您表达此处的null是否是一个好主意。

因此,Mads概述的解决方案是:

  1. 我们认为,希望引用不为null更为常见。可空引用类型将是较少见的类型(尽管我们没有足够的数据来告诉我们多少),因此它们是需要新注释的类型。

  2. 该语言已经具有可空值类型的概念和语法。两者之间的类比将使添加语言在概念上更容易,在语言上也更简单。

  3. 除非您已经积极地决定要它们,否则似乎不应该给笨拙的null值加重负担,这对您或您的消费者来说是正确的。空,而不是没有空,应该是您明确选择加入的东西。

所需功能的示例:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

该预览版可用于Visual Studio 2017 15.5.4+预览版。


有谁知道它是否最终成为C#8.0的一部分?
TroySteven

@TroySteven是的,您必须通过Visual Studio中的设置选择加入。
格雷格


很好,因此看起来您必须先启用它,然后才能向后兼容。
TroySteven

15

我知道这是一个非常古老的问题,但是这里没有这个问题:

如果您使用ReSharper / Rider,则可以使用带注释的框架

编辑:我只是对此答案随机-1。没关系。请注意,即使它不再是C#8.0 +项目的推荐方法,它仍然有效(要了解原因,请参阅Greg的答案)。


1
有趣的是,默认情况下,已编译的应用程序将不引用JetBrains.Annotations.dll,因此您不必随应用程序一起分发它:如何使用JetBrains注释来改进ReSharper检查
Lu55

9

在企业库中检出验证器。您可以执行以下操作:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

然后在您想要验证的代码中:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);

0

不是最漂亮,但:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

您也可以在ContainsNullParameters方法中获得更多创意:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

当然,您可以使用拦截器或反射器,但这些方法易于遵循/使用,且开销很小


3
使用这样的查询的非常不好的做法:return (from o in methodParams where o == null).Count() > 0; 使用:return methodParams.Any(o=>o==null);在大型馆藏中它会快得多
Yavanosta 2012年

为什么要传出异常?为什么不就扔呢?
Martin Capodici

-4

好的,这个回复有点晚了,但是这就是我的解决方法:

public static string Default(this string x)
{
    return x ?? "";
}

使用此扩展方法,您可以将null和empty字符串视为同一对象。

例如

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

我知道这不太理想,因为您必须记住在任何地方都调用default,但这是一种解决方案。


5
有什么比(x == null)检查更好/更容易的方法?或String.IsNotNullOrEmpty函数。
巴达维亚2014年

string.IsNotNullOrEmpty除了让您在意的事情留在左边,这比真正的要好得多。可以输入其他函数(长度,串联)等。稍微好一点。
马丁·卡波迪奇

@MartinCapodici同样,这也会产生一种幻觉,即您可以安全地Default()nullmodel.Day)上调用实例方法()。我知道扩展方法不会针对null进行检查,但是我的眼睛没有意识到,他们已经在想象?代码中的内容了:model?.Day?.Default():)
Alb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.