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.
source to share
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.
source to share
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.
source to share