I've written two abstract classes that represent the base class for entities: One where the Id
property is an int, another that allows to specify the type of the Id
property using a generic type parameter 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; }
}
These classes are defined in a core assembly which I use in nearly all projects I work on. Since C# 8.0 came out, I've experimented with enabling nullable reference types, which has worked out well so far.
However, in the case of BaseEntity<TId>
, the compiler gives a warning:
Non-nullable property 'Id' is uninitialized. Consider declaring the property as nullable.
I understand the warning, but I can't seem to fix the issue for my use case. More specifically, I want to allow declaration of types that derive from:
System.String
, i.e.BaseEntity<string>
- Any value type, e.g.
BaseEntity<System.Guid>
or a custom struct
Since System.String
isn't a value type, this doesn't seem to be possible: If I constrain TId
to structs (BaseEntity<TId> where TId : struct
), I can't declare BaseEntity<string>
any more.
The only solution (?) I've found so far to disable the warning, is initializing the Id
property with its default value and using the !
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!;
}
However, I'd like to make the intent of the code clear: That TId
can be a value type (e.g. short, long, System.Guid
, ...), OR a System.String
.
Is this somehow possible?
TId
, you cans usewhere TId : notnull
to prohibitclass Entity : BaseEntity<string?>
usages. – Orace