Home > Net >  Why can't interface be used as type argument?
Why can't interface be used as type argument?

Time:08-01

public abstract record Result<T, ErrorT>
{
    private Result() { }
    public sealed record Ok(T result) : Result<T, ErrorT>;
    public sealed record Error(ErrorT error) : Result<T, ErrorT>;
}

public interface IValid<T>
{
    T Value { get; init; }
    abstract static IEnumerable<string> Validate(T obj);
    abstract static Result<IValid<T>, IEnumerable<string>> Create(T value);
}

I am trying to create a validation pattern that creates validated values of type T. But the Create function throws a compiler error CS8920

The interface Valid cannot be used as type argument

Any reason why this is not allowed and is there a way around this?

Example usage of this would be

public record DriverLicense : IValid<string>
{
    public string Value { get; init; } = null!;

    private DriverLicense() { }

    public static Result<DriverLicense, IEnumerable<string>> Create(string licenseNumber){...}
    public static IEnumerable<string> Validate(string licenseNumber){...}
}

CodePudding user response:

This code alone:

public abstract record Result<T, ErrorT>
{
    private Result() { }
    public sealed record Ok(T result) : Result<T, ErrorT>;
    public sealed record Error(ErrorT error) : Result<T, ErrorT>;
}
public interface IValid<T>
{
    T Value { get; init; }
    abstract static IEnumerable<string> Validate(T obj);
    abstract static Result<IValid<T>, IEnumerable<string>> Create(T value);
}

did compile when they first added static abstract interface members, as can be seen on SharpLab, if you select the "C# Next: Static Abstract Members In Interfaces" branch.

However, this signature for Create is not what you actually mean. Your intended implementation doesn't actually implement Create:

// interface
abstract static Result<IValid<T>, IEnumerable<string>> Create(T value);
// implementation
public static Result<DriverLicense, IEnumerable<string>> Create(string licenseNumber){}

Notice that the return types are different, unrelated types. The type parameters of records are invariant. This would not have compiled in any version of C#.

What you actually meant is:

public interface IValid<TSelf, T> where TSelf: IValid<TSelf, T>
{
    ...
    abstract static Result<TSelf, IEnumerable<string>> Create(T value);
}

public record DriverLicense : IValid<DriverLicense, string>
{
    ...
    public static Result<DriverLicense, IEnumerable<string>> Create(string licenseNumber) {...}
}

See also:

My guess is that they changed it in a later version, so that an error message would now show up as soon as you try to write the incorrect interface, rather than show the "does not implement this method" error when you write the implementation later down the line.

  • Related