是否有可能需要开放通用接口的通用约束?

时间:2014-08-14 08:14:15

标签: c# generics type-inference

我正在实施一个存储库,并一直想知道如何让它对用户更友好一些。现在我有一个IEntity接口,指定Id字段:

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

我的存储库允许用户通过该ID获取新实例。现在它可以处理的类型需要实现IEntity接口,所以我对存储库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;
    }
}

TU之间存在明显的关系,编译器很好地理解了错误用法,但还不足以启用类型推断 - 每次调用Get都需要指定通用参数明确。我知道无法指定T,但如何改进方法签名以便不需要指定U?现在我有一个最常用的过载:

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

我想知道是否有可能以某种方式指定一个开放的通用接口作为约束,或者对于一般情况更好的方法签名。

2 个答案:

答案 0 :(得分:2)

您可以使用扩展方法做些有趣的事情。

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

这将被称为如下。

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

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

您可以将get方法放在任何您喜欢的位置,但AssignId方法必须位于有资格拥有扩展方法的类中,即它必须是静态非泛型类,并且您可能需要一个引用的using语句到包含扩展方法的命名空间。

答案 1 :(得分:2)

在阅读Partial generic type inference possible in C#?Working around lack of partial generic type inference with constraints之后,我认为Marc Gravell的解决方案最接近任何合理的解决方案。通过辅助类(用于捕获第一个参数的类型)和Grax建议的扩展方法推断来获取他的部分泛型参数应用程序,我最终得到了Repository的实现

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);
    }
}

带帮助者

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);
    }
}

然后用法如下:

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

据我所知,泛型参数推断现在如何在C#中起作用,因此无法进行部分推理。