“Type”无法满足参数“TParam”的“new()”约束,因为“Type”具有必需的成员

Mr *_*nce 4 c# generics .net-7.0

我有这个类结构(简化):

public class InducingMedium
{
   public required string File { get; set; }
}

public class InducingVideo : InducingMedium {}
public class InducingAudio : InducingMedium {}
Run Code Online (Sandbox Code Playgroud)

现在,我想一般实例化特定类型的实例:

public abstract class BaseInducingTests<TMedium>
    where TMedium : InducingMedium, new()
{
   protected async Task<IEnumerable<TMedium>> CreateInducingMedia(IEnumerable<string> files)
   {
      return files.Select(file =>
      {
          // Do some processing...

          return new TMedium
          {
              File = file,
          };
      });
   }
}

public class InducingVideosTests : BaseInducingTests<InducingVideo>
{
}
Run Code Online (Sandbox Code Playgroud)

但在派生类中我收到错误:

'Namespace.InducingVideo' cannot satisfy the 'new()' constraint 
on parameter 'TMedium' in the generic class 'Namespace.Tests.BaseInducingTests<TMedium>' 
because 'Namespace.InducingVideo' has required members
Run Code Online (Sandbox Code Playgroud)

有什么办法可以在不引入反射的情况下解决这个问题吗?
我对成员感到非常兴奋required,它与可为 null 的类型配合得很好,但现在我发现这有它自己的警告:(

Gur*_*ron 5

文档中明确提到了这一点:

当类型参数包含 new() 约束时,具有任何必需成员的类型不能用作类型参数。编译器无法强制在通用代码中初始化所有必需的成员。

删除required修饰符或更改通用处理。例如通过ctor提供工厂:

public abstract class BaseInducingTests<TMedium>
    where TMedium : InducingMedium
{
    private readonly Func<string, TMedium> _init;

    public BaseInducingTests(Func<string, TMedium> init)
    {
        _init = init;
    }
    protected async Task<IEnumerable<TMedium>> CreateInducingMedia(IEnumerable<string> files)
    {
        return files.Select(file => _init(file));
    }
}

public class InducingVideosTests : BaseInducingTests<InducingVideo>
{
    public InducingVideosTests() : base(s => new InducingVideo{File = s})
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

如果需要,您可以创建一个包装器来支持满足new()约束的类:

public abstract class BaseNewableInducingTests<TMedium> : BaseInducingTests<TMedium>
    where TMedium : InducingMedium, new()
{
    protected BaseNewableInducingTests() : base(s => new TMedium { File = s })
    {
    }
}
Run Code Online (Sandbox Code Playgroud)