Nullable reference types: How to specify “T?” type without constraining to class or struct

后端 未结 3 1751
别跟我提以往
别跟我提以往 2020-11-28 09:59

I want to create a generic class that has a member of type T. T may be a class, a nullable class, a struct, or a nullable struct. So basically anyt

3条回答
  •  南方客
    南方客 (楼主)
    2020-11-28 10:30

    What to do if you are using C# 9

    In C# 9, you can use T? on an unconstrained type parameter to indicate that the type is always nullable when T is a reference type. In fact, the example in the original question "just works" after adding ? to the property and constructor parameter. See the following example to understand what behaviors you may expect for different kinds of type arguments to Box.

    var box1 = Box.CreateDefault();
    // warning: box1.Value may be null
    box1.Value.ToString();
    
    var box2 = Box.CreateDefault();
    // warning: box2.Value may be null
    box2.Value.ToString();
    
    var box3 = Box.CreateDefault();
    // no warning
    box3.Value.ToString();
    
    var box4 = Box.CreateDefault();
    // warning: 'box4.Value' may be null
    box4.Value.Value.ToString();
    

    What to do if you are using C# 8

    In C# 8, it is not possible to put a nullable annotation on an unconstrained type parameter (i.e. that is not known to be of a reference type or value type).

    As discussed in the comments on this question, you will probably need to take some thought as to whether a Box with a default value is valid or not in a nullable context and potentially adjust your API surface accordingly. Perhaps the type has to be Box in order for an instance containing a default value to be valid. However, there are scenarios where you will want to specify that properties, method returns or parameters, etc. could still be null even though they have non-nullable reference types. If you are in that category, you will probably want to make use of nullability-related attributes.

    The MaybeNull and AllowNull attributes have been introduced to .NET Core 3 to handle this scenario.

    Some of the specific behaviors of these attributes are still evolving, but the basic idea is:

    • [MaybeNull] means that the output of something (reading a field or property, a method return, etc.) could be null.
    • [AllowNull] means that the input to something (writing a field or property, a method parameter, etc.) could be null.
    #nullable enable
    using System.Diagnostics.CodeAnalysis;
    
    class Box
    {
        // We use MaybeNull to indicate null could be returned from the property,
        // and AllowNull to indicate that null is allowed to be assigned to the property.
        [MaybeNull, AllowNull]
        public T Value { get; }
    
        // We use only AllowNull here, because the parameter only represents
        // an input, unlike the property which has both input and output
        public Box([AllowNull] T value)
        {
            Value = value;
        }
    
        public static Box CreateDefault()
        {
            return new Box(default);
        }
    
        public static void UseStringDefault()
        {
            var box = Box.CreateDefault();
            // Since 'box.Value' is a reference type here, [MaybeNull]
            // makes us warn on dereference of it.
            _ = box.Value.Length;
        }
    
        public static void UseIntDefault()
        {
            // Since 'box.Value' is a value type here, we don't warn on
            // dereference even though the original property has [MaybeNull]
            var box = Box.CreateDefault();
            _ = box.Value.ToString();
        }
    }
    

    Please see https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types for more information, particularly the section "the issue with T?".

提交回复
热议问题