generics - 与 C# 8.0 可空引用类型结合时,是否可以为值或字符串类型声明泛型类型约束?

标签 generics c#-8.0 nullable-reference-types

我写了两个抽象类来代表实体的基类:一个是 Id属性是 整数 , 另一个允许指定 Id 的类型使用泛型类型参数的属性 TId :

/// <summary>
///     Represents the base class for all entities.
/// </summary>
[System.Serializable]
public abstract class BaseEntity
{
    /// <summary>
    ///     Gets or sets the ID of the entity.
    /// </summary>
    public int Id { get; set; }
}

/// <summary>
///     Represents the base class for all entities that have an ID of type <typeparamref name="TId"/>.
/// </summary>
/// <typeparam name="TId">
///     The type of the <see cref="Id"/> property.
/// </typeparam>
[System.Serializable]
public abstract class BaseEntity<TId>
{
    /// <summary>
    ///     Gets or sets the ID of the entity.
    /// </summary>
    public TId Id { get; set; }
}

这些类是在我从事的几乎所有项目中使用的核心程序集中定义的。自从 C# 8.0 发布以来,我已经尝试启用 nullable reference types ,到目前为止效果很好。

但是,对于 BaseEntity<TId> ,编译器给出警告:

Non-nullable property 'Id' is uninitialized. Consider declaring the property as nullable.



我理解这个警告,但我似乎无法为我的用例解决这个问题。更具体地说,我想允许声明派生自的类型:
  • System.String ,即 BaseEntity<string>
  • 任何值类型,例如BaseEntity<System.Guid>或自定义结构

  • 由于System.String不是值类型,这似乎是不可能的:如果我约束 TId对于结构( BaseEntity<TId> where TId : struct ),我不能声明 BaseEntity<string>不再。

    到目前为止,我发现的唯一解决方案(?)禁用警告,是初始化 Id属性及其默认值并使用 !运算符(operator):

    /// <summary>
    ///     Represents the base class for all entities that have an ID of type <typeparamref name="TId"/>.
    /// </summary>
    /// <typeparam name="TId">
    ///     The type of the <see cref="Id"/> property.
    /// </typeparam>
    [System.Serializable]
    public abstract class BaseEntity<TId>
    {
        /// <summary>
        ///     Gets or sets the ID of the entity.
        /// </summary>
        public TId Id { get; set; } = default!;
    }
    

    但是,我想明确代码的意图:TId可以是值类型(例如,短、长、System.Guid 、...)、一个 System.String .

    这有可能吗?

    最佳答案

    不,没有这样的约束——无论您是否使用可为空的引用类型。

    您可能会做的是使用私有(private)构造函数来确保只有在基类型中声明的类型才能从 BaseEntity 派生。 ,然后使用两个特定版本:

    public abstract class BaseEntity<TId>
    {
        public TId Id { get; set; }
    
        private BaseEntity<TId>(Id id) => Id = id;
    
        public class StructEntity<T> : BaseEntity<T> where T : struct
        {
            public StructEntity() : base(default) {}
        }
    
        public class StringEntity : BaseEntity<string>
        {
            public StringEntity(string id) : base(id) {}
        }
    }
    

    那仍然可以让您使用 BaseEntity<T>在大多数地方,但任何时候你想构建一个实体,你都需要在这两者之间进行选择。

    我不知道这将如何与支持序列化联系起来,尽管我个人还是会避开二进制序列化。

    关于generics - 与 C# 8.0 可空引用类型结合时,是否可以为值或字符串类型声明泛型类型约束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59933006/

    相关文章:

    c# - 我可以在要创建的对象的类对泛型类型参数有约束的泛型类中创建泛型对象吗?

    c# - 特征递归模式目前在 Vs Code 的预览版中

    c# - 启用可空类型没有区别

    c# - 泛型还是只使用重载?

    python - 通用迭代器注释 Python

    c# - IAsyncEnumerable<> 在 VS 2019 预览版 2(Core 3.0 预览版 1)中损坏

    c# - 使用 [DisallowNull] 与不可为空的引用类型

    c# - 可空引用类型和 Either 模式

    c#-8.0 - C# 开关表达式 null 情况

    c# - 使用泛型进行类型转换