I'm implementing a repository and keep wondering about making it a bit more friendly to the user. Right now I have an IEntity
interface that specifies an Id
field:
public interface IEntity<T>
{
T Id { get; set; }
}
And my repository allows users to get an new instance by that id. Now the types it can handle need to implement the IEntity
interface, so I have a generic constraint on the repository Get
method:
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's an obvious relation between T
and U
and compiler understands it well enough to flag miss-usages, but not enough to enable type inference - each call to Get
requires specifying the generic parameters explicitly. I know there's no way around specifying T
, but how can I improve the method signature so that specifying U
is not required? Right now I have an overload for the most common usage:
public T Get<T>(int id) where T : IEntity<int>
{
return Get<T, int>(id);
}
I'm wondering if it is possible to somehow specify an open generic interface as a constraint or what would be a better method signature for the general case.
After reading Partial generic type inference possible in C#? and Working around lack of partial generic type inference with constraints, I'm thinking that Marc Gravell's solution is the closest to any reasonable. Taking his partial generic parameter application via a helper class (which is used to capture the type of the first parameter) and the extension method inference that Grax suggested, I end up with a Repository
implementation of
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 a helper
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);
}
}
The usage then looks like that:
var intEntity = repository.Get<IntEntity>().ById(19);
var guidEndtity = repository.Get<GuidEntity>().ById(Guid.Empty);
As far as I understand how generic parameter inference works in C# right now, it's not possible to get a partial inference.
You can do something fun with extension methods here.
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;
}
}
This would be called as follows.
var result = Extensions.Get<EntityInt>().AssignId(34);
var result2 = Extensions.Get<EntityString>().AssignId("WALKEN");
You can put your get method anywhere you like but the AssignId method would have to be in a class that is qualified to have extension methods, i.e. it must be a static non-generic class and you may need a using statement that refers to the namespace containing the extension method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With