C# 泛型类层次结构中可为空的构造函数参数

问题描述 投票:0回答:1

考虑 EF Core 实体的通用类层次结构:

public abstract class Parent<TUserKey>
  where TUserKey : IEquatable<TUserKey>  // same signature as IdentityUser<TKey>
{
  protected Parent() { }                 // non-public ctor for use by EF

  protected Parent(TUserKey? userId) { UserId = userId; }

  public long Id { get; }
  public TUserKey? UserId { get; }      // nullable because optional relationship
  // other properties...
}


public sealed class Child : Parent<long>
{
  private Child() : base() { }          // non-public ctor for use by EF

  public Child(long? userId) : base(userId) { }       // <----- PROBLEM HERE
  // other properties...
}

这不会编译:

无法从“长”转换?到“长”

想法:

  1. 我可以用
    where TUserKey : struct, IEquatable<TUserKey>
    声明父类。然而,这是不合适的,因为某些子类使用
    string
    作为类型。
  2. 我可以将子类的 ctor 更改为
    public Child(long userId) : base(userId) { }
    。然而,许多调用站点都会传递
    long?
    ,所以我需要在任何地方都使用
    userId!

我怀疑我被(2)困住了,但也许我忽略了一些东西。有没有更好的方法,无需进行重大重写?

(顺便说一句:

TUserKey : IEquatable<TUserKey>
的目的是匹配
IdentityUser<TKey>
的签名。)

c# generics .net-8.0 nullable-reference-types c#-12.0
1个回答
0
投票

正如@LasseV.Karlsen 的评论中所解释的,可为空的开放通用参数是问题的根源。

这段代码实际上包含在我们主系统使用的帮助程序库中,因此它需要灵活。一些消费者使用值类型作为 PK(例如 int、long、Guid),而其他消费者则使用引用类型(例如字符串)。

因此,另一种方法是将层次结构一分为二:一个用于值,一个用于引用类型。

基类:

public abstract class Parent
{
  protected Parent() { }
  public long Id { get; }
  // other properties...
}


public abstract class ParentValue<TUserKey> : Parent
  where TUserKey : struct, IEquatable<TUserKey>
{
  protected ParentValue() { }
  protected ParentValue(TUserKey? userId) { UserId = userId; }
  public TUserKey? UserId { get; }
}


public abstract class ParentReference<TUserKey> : Parent
  where TUserKey : class, IEquatable<TUserKey>
{
  protected ParentReference() { }
  protected ParentReference(TUserKey? userId) { UserId = userId; }
  public TUserKey? UserId { get; }
}

消费者将定义一个子类。对于值类型:

public sealed class Child : ParentValue<long>
{
  private Child() : base() { }
  public Child(long? userId) : base(userId) { }
}

或参考类型:

public sealed class Child : ParentReference<string>
{
  private Child() : base() { }
  public Child(string? userId) : base(userId) { }
}
© www.soinside.com 2019 - 2024. All rights reserved.