Home > Blockchain >  'Type' cannot satisfy the 'new()' constraint on parameter 'TParam' bec
'Type' cannot satisfy the 'new()' constraint on parameter 'TParam' bec

Time:01-28

I have this class structure (simplified):

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

public class InducingVideo : InducingMedium {}
public class InducingAudio : InducingMedium {}

Now, I want to generically instantiate an instance of a specific type:

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>
{
}

But in the derived class I get an error:

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

Is there any way to fix this without introducing reflection?
I was really excited about required members, which work pretty well with nullable types, but now I see this has its own caveats :(

CodePudding user response:

This is explicitly mentioned in the docs:

A type with any required members may not be used as a type argument when the type parameter includes the new() constraint. The compiler can't enforce that all required members are initialized in the generic code.

Either remove the required modifier or change the generic handling. For example provide factory via 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})
    {
    }
}

If needed you can then create a wrapper to support classes which satisfy new() constraint:

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

CodePudding user response:

Another workaround which I've just found is SetsRequiredMembers attribute:

public class InducingVideosTests : BaseInducingTests<TestInducingVideo>
{
}

public class TestInducingVideo : InducingVideo
{
    [SetsRequiredMembers]
    public TestInducingVideo()
    {
    }
}

This kind of defeats the purpose of required members, but so does reflection and for test purposes it's an easier way.

Source: https://code-maze.com/csharp-required-members/

  • Related