简体   繁体   English

C#中可为空的字符串

[英]nullable string in C#

I know a string in C# can be 'nullable' by just using null. 我知道只需使用null,C#中的字符串就可以为“ nullable”。

However, the whole point of nullable types is that I get help from the compiler [edit : aka, type error : can't add apple to bananas] 但是,可空类型的全部要点是我从编译器获得帮助[编辑:aka,类型错误:无法将苹果添加到香蕉中]

And using this 'type system hack-ish' of 'nullability depends on the underlying type' breaks whatever guarantee I might have (which is no small feat as that seems to be the whole point of using a type system in the first place..) 而使用这种“类型系统”的“空性取决于底层类型”会破坏我可能拥有的任何保证(这并非易事,因为这似乎首先就是使用类型系统的全部要点。 )

What are the standard way to deal with this in C#, if one wants to ? 如果愿意,用C#处理此问题的标准方法是什么? Shall I just roll my own 'nullable' class ? 我是否要开设自己的“可空”课程?

Edit 编辑

Let me rephrase the question : 我再改一下这个问题:

What is the standard way, in C#, to make sure that a variable that you annotated as nullable, does not get assigned to a variable that you did not annotate as nullable. 在C#中,确保被注释为可为空的变量不会分配给未注释为可为空的变量的标准方法是什么?

That is : what is the standard way to have for all types , precisely what the ? 那就是: 所有类型的标准方式是什么,究竟是什么 keyword gives you for value type. 关键字为您提供值类型。

If you are asking about a way to ensure that the return type of a certain method is not null , there is a solution: That method has to return a value type. 如果您要询问确保某个方法的返回类型不为null方法,则有一个解决方案:该方法必须返回值类型。 Every method that returns a reference type can potentially return null . 每个返回引用类型的方法都可能返回null There is nothing you can do about it. 您无能为力。

So, you could create a struct like this to be able to return a value type: 因此,您可以创建一个这样的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; }
    }
}

Your method that is supposed to return a string now can return SurelyNotNull<string> . 现在应该返回字符串的方法可以返回SurelyNotNull<string>

The problem with this approach is: 这种方法的问题是:
It doesn't work. 没用 While the return value of the method is guaranteed to be not null , the return value of SurelyNotNull<T>.Value is not. 虽然保证方法的返回值不为null ,但SurelyNotNull<T>.Value的返回值不为。 At first glance, it looks like it is guaranteed to be not null. 乍一看,似乎可以保证它不为null。 But it can be, because of this: 但是可以这样,因为:
Every struct has an implicit, public, parameterless constructor, even when another constructor is defined . 每个结构都有一个隐式的,公共的,无参数的构造函数, 即使定义了另一个构造函数也是如此
The following code is valid and compiles with the struct from above: 以下代码有效,并使用上面的struct编译:

new SurelyNotNull<string>();

Conclusion: 结论:
In C#, you are unable to achieve what you are trying to do. 在C#中,您无法实现您想做的事情。
You can still use this approach, you just need to understand that someone could use this type and still produce a null value. 您仍然可以使用这种方法,您只需要了解有人可以使用此类型并且仍然产生空值。 To fail fast in this scenario, it would be a good idea to add a check to the getter of Value and throw an exception if _value is null. 要在这种情况下快速失败,最好将检查添加到Value的getter并在_value为null时引发异常。

As Daniel Hilgarth pointed out, there's no bullet-proof way to achieve this. 正如丹尼尔·希尔加斯(Daniel Hilgarth)所指出的,没有实现这一目标的防弹方法。 My proposal is similar to his, but with some added safety. 我的提议与他的提议相似,但又增加了一些安全性。 You can decide for yourself if the benefits outweigh the cost of using a wrapper type throughout your program. 您可以自己决定收益是否超过在整个程序中使用包装器类型的成本。

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);
    }
}

The wrapper type is primarily to make your intent self-documenting, so it's unlikely you would bypass it with a zero-initialized struct, but it keeps a flag to indicate it was initialized correctly anyway. 包装器类型主要是为了使您的意图能够自我记录,因此不太可能使用零初始化的结构来绕过它,但是它会保留一个标志以指示它是否已正确初始化。 In that admittedly unusual case it will throw an ArgumentNullException when accessing the 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, "");
    }
}

Now, is this better than an occasional NRE? 现在,这比偶尔的NRE好吗? Maybe. 也许。 It can save a lot of boilerplate arg checking. 它可以节省大量样板参数检查。 You'll need to decide if it's worthwhile for your situation. 您需要确定是否适合您的情况。 Short of compiler support, like F# provides, there's no way to provide compile-time null safety, but you can (arguably) ease run-time safety. 缺少像F#这样的编译器支持,无法提供编译时的空安全性,但是(可以说)您可以减轻运行时的安全性。

You may want to up-vote this issue on Microsoft's customer feedback site: Add non-nullable reference types in C# 您可能希望在Microsoft的客户反馈网站上投票解决此问题: 在C#中添加不可为空的引用类型

You want something like a NotNull attribute. 您需要类似NotNull属性的东西。 Using these will give you compile time warnings and errors and in some cases IDE feedback about assigning NULL values to NotNull attributes. 使用这些将为您提供编译时警告和错误,并且在某些情况下,IDE会反馈有关将NULL值分配给NotNull属性的信息。

See this question : C#: How to Implement and use a NotNull and CanBeNull attribute 看到这个问题: C#:如何实现和使用NotNull和CanBeNull属性

All reference types are nullable. 所有引用类型都可以为空。 String is a reference type. 字符串是引用类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM