Is it possible to have a generic constraint requiring an open generic interface?

I am implementing a repository and wondering how to make it more user-friendly. Right now I have an interface IEntity

that specifies a field Id

:

public interface IEntity<T>
{
    T Id { get; set; }
}

      

And my repository allows users to get a new instance using that id. Now the types it can handle must implement the interface IEntity

, so I have a general constraint on the repository method Get

:

public class Repository
{
    public T Get<T, U>(U id) where T: IEntity<U>
    {
        // fake implementation, but T is required at compile time
        var result = Activator.CreateInstance<T>();
        result.Id = id;
        return result;
    }
}

      

There is an obvious connection between T

and U

, and the compiler understands it well enough to specify the whitespace, but not enough to enable type inference — each call Get

requires you to explicitly specify general parameters. I know there is no way to specify T

, but how can I improve the method signature so that no indication U

is required? Right now I have an overload for the most common use:

public T Get<T>(int id) where T : IEntity<int>
{
    return Get<T, int>(id);
}

      

I'm wondering if it is possible in some way to define an open generic interface as a constraint, or what would be the best method signature for the general case.

+3


source to share


2 answers


After reading Possible incomplete output type in C #? and Dealing with the lack of partial typical inference with constraints , I think Mark Gravell's solution is closest to any reasonable one. Taking his partial shared parameter application through a helper class (which is used to capture the type of the first parameter) and the output of the extension method as suggested by Grax, I end up implementingRepository

public class Repository
{
    public T Get<T, TId>(TId id) where T: IEntity<TId>
    {
        // fake implementation, but T is required at compile time
        var result = Activator.CreateInstance<T>();
        result.Id = id;
        return result;
    }

    public GetHelper<T> Get<T>()
    {
        return new GetHelper<T>(this);
    }
}

      

with assistant

public struct GetHelper<T>
{
    internal readonly Repository Repository;

    public GetHelper(Repository repository)
    {
        Repository = repository;
    }
}

public static class RepositoryExtensions
{
    public static T ById<T, TId>(this GetHelper<T> helper, TId id)
      where T : IEntity<TId>
    {
        return helper.Repository.Get<T, TId>(id);
    }
}

      



Then the usage looks like this:

var intEntity = repository.Get<IntEntity>().ById(19);
var guidEndtity = repository.Get<GuidEntity>().ById(Guid.Empty);

      

As far as I understand, how general parameter output works in C # right now, it is not possible to get partial output.

+2


source


You can do something interesting here with extension methods.

public static class Extensions
{
    public static T Get<T>()
    {
        // fake implementation, but T is required at compile time
        var result = Activator.CreateInstance<T>();
        return result;
    }

    public static T AssignId<T, U>(this T entity, U id)
        where T : IEntity<U>
    {
        entity.Id = id;
        return entity;
    }
}

      

It will be called like this.



var result = Extensions.Get<EntityInt>().AssignId(34);

var result2 = Extensions.Get<EntityString>().AssignId("WALKEN");

      

You can put your getter anywhere, but the AssignId method must be in a class that is qualified to use extension methods, i.e. it must be a static unrelated class and you might need a using statement that references the namespace that contains the extension method.

+2


source







All Articles