简体   繁体   English

泛型类中的可空类型

[英]Nullable types in generic class

I need to create a class similar to this: 我需要创建一个与此类似的类:

class GenericClass<T>
{
    public T[] Arr {get; }
    public GenericClass(int n)
    {
        Arr = new T[n];
        for (int i = 0; i < n; i++)
        {
            Arr[i] = null;
        }
    }
}

But there is a compiler error: 但是有一个编译器错误:

CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. CS0403无法将null转换为类型参数'T',因为它可能是非空值类型。 Consider using 'default(T)' instead. 考虑改用“ default(T)”。

I don't want to use default(T) , because it could be the same as normal value but I need to distinguish. 我不想使用default(T) ,因为它可能与正常值相同,但是我需要区分。 I don't know the type so I can't use minimal value. 我不知道类型,所以我不能使用最小值。 How can I use null? 如何使用null?

You are actually shooting yourself in the foot here. 您实际上是在这里射击自己。 Arrays in .NET are automatically set to the default value of the type, so as long as a type's default is null (all objects and nullables), then simply creating the array is enough to set all items to null. .NET中的数组会自动设置为该类型的默认值,因此,只要类型的默认值为null(所有对象和可为null的对象),那么只需创建数组就足以将所有项目设置为null。 Without that excess code, you may never have encountered an issue at all, depending on how you're trying to use this class. 没有这些多余的代码,您可能根本不会遇到任何问题,这取决于您尝试使用此类的方式。

If you're trying to allow cases like GenericClass<int> and have it expose an array of int? 如果您试图允许类似GenericClass<int>并使它公开一个int?数组int? s, then do this. s,然后执行此操作。 The where T : struct enforces that the supplied type is a value type, thereby allowing you to use T? where T : structwhere T : struct强制提供的类型是值类型,从而允许您使用T? . You can't use object types for the generic parameter in this case. 在这种情况下,不能为通用参数使用对象类型。

class GenericClass<T> where T : struct
{
    public T?[] Arr { get; }
    public GenericClass(int n)
    {
        Arr = new T?[n];
    }
}

If you're trying to create a class that supports both object instances and nullables, try this. 如果要创建一个同时支持对象实例和可为空的类,请尝试此操作。 The problem here is you can't make a type constraint -- if you use where T : struct , you can't use object types; 这里的问题是您不能设置类型约束-如果使用where T : struct ,则不能使用对象类型。 and if you use where T : class , you can't use nullables. 如果使用where T : class ,则不能使用可为空的值。 Therefore, this type allows usages like GenericClass<MyObject> (works like you want), GenericClass<int?> (works like you want) and GenericClass<int> (doesn't work like you want, and can't possibly work like you want anyway). 因此,此类型允许使用GenericClass<MyObject> (可以GenericClass<MyObject>运行), GenericClass<int?> (可以GenericClass<int> )和GenericClass<int> (不能按需要运行,也不能像无论如何你都想要)。 Unfortunately, there's no way to define this generic to disallow the latter case. 不幸的是,无法定义此泛型来禁止后一种情况。 You can make that check at runtime if you like, though. 不过,您可以根据需要在运行时进行检查。

class GenericClass<T>
{
    public T[] Arr { get; }
    public GenericClass(int n)
    {
        Arr = new T[n];
    }
}

The thing with generics is that they have to consider the possibility of T being literally anything, including a type that cannot be null (value types including primitives and structs). 泛型的问题在于,他们必须考虑T确实是任何东西的可能性,包括不能为null类型(值类型包括基元和结构)。 In order to restrict the generic parameter to types that can be null , you need to add the class restraint: 为了将泛型参数限制为可以null类型,您需要添加class约束:

class GenericClass<T> where T : class
{
    public T[] Arr { get; private set; }
    public GenericClass(int n)
    {
        Arr = new T[n];
        for (int i = 0; i < n; i++)
        {
            Arr[i] = null;
        }
    }
}

Alternatively, you may not want to be dealing with null at all. 另外,您可能根本不想处理null Instead, you can replace 相反,您可以更换

Arr[i] = null;

with

Arr[i] = default(T);

and it will work fine. 它将正常工作。 default(T) will return null for any nullable type and the default value for any non-nullable type. 对于任何可为空的类型, default(T)将返回null ;对于任何不可为空的类型, default(T)将返回默认值。 (0 for int , false for bool , etc.) (对于int为0,对于bool为false,等等)

EDIT: As another alternative, you can represent the object internally with a Nullable wrapper type. 编辑:作为另一种选择,您可以在内部用Nullable包装器类型表示对象。 C# allows a shorthand for this syntax with the ? C#允许使用?来简化此语法? operator: 操作员:

class GenericClass<T>
{
    public T?[] Arr { get; private set; }
    public GenericClass(int n)
    {
        Arr = new T?[n];
    }
}

Incidentally, with this approach, there's no need to iterate over every index of the array and set it to null , as C# will handle that for you. 顺便说一句,使用这种方法,无需遍历数组的每个索引并将其设置为null ,因为C#将为您处理。

If you know that T must be a nullable type, say, Nullable<Xyz> , you can force your users to pass Xyz instead, and make Nullable<Xyz> (aka Xyz? ) internally: 如果您知道T 必须是可为空的类型,例如Nullable<Xyz> ,则可以强制用户传递Xyz ,并在内部设置Nullable<Xyz> (aka Xyz? ):

class GenericClass<T> where T : struct {
    public T?[] Arr {get; }
    public GenericClass(int n) {
        Arr = new T?[n]; // This will create an array of nulls, no need for a loop
    }
}

The where T : struct constraint added to the declaration will prevent GenericClass<T> instantiations with arguments that are not value types. 添加到声明中的where T : struct约束将防止使用值类型参数的GenericClass<T>实例化。 For example, an attempt to make GenericType<string> will result in compile-time error. 例如,尝试使GenericType<string>会导致编译时错误。

What you want to do is instantiate your class with a nullable type if it's a value type: 您想要做的是使用可为空的类型实例化您的类(如果它是值类型):

var obj = new GenericClass<int?>();

Then you can use default(T) instead of null without issue. 然后,您可以使用default(T)而不是null而不会出现问题。 You'll just end up with an int?[] array though you don't need to use it explicitly because each element in the array will automatically get the default value when the array is instantiated. 尽管不需要显式使用它,但最终将得到一个int?[]数组,因为在实例化数组时,数组中的每个元素都会自动获得默认值。

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

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