具有接口类型约束的 C# 泛型方法

Posted

技术标签:

【中文标题】具有接口类型约束的 C# 泛型方法【英文标题】:C# generic method with interface type constraint 【发布时间】:2013-01-29 09:59:12 【问题描述】:

假设我有:

通用方法Get<T> 几个接口IEntity,IValue 分别实现这些接口的几个类,例如:Entity -> IEntityValue -> IValue 等。

=> Get<T> 方法有没有办法只允许接口作为泛型类型?

Get<IEntity>(42); //Allowed
Get<Entity>(42);  //Compiler error

我当前的解决方案如下所示:

具有类型约束where T: IPersistable 的泛型方法Get&lt;T&gt;(以防止大多数类型作为参数传递) 接口实现IPersistable

函数主动检查类型:

public T Get<T>(long id) where T : IPersistable

   if (typeof (T) == typeof (IEntity))
      return (T) EntityDao.Get(id);
   if (typeof (T) == typeof (IValue))
      return (T) ValueDao.Get(id);

   //...

   throw new TechnicalException("Type not supported");

=> 问题是:

    它不干净......我可以忍受,因为只有很少的类型可供检查 签名与函数实际所做的不匹配。它允许IPersistable 进入,但不是真的这真的让我很烦:(

编辑:我正在考虑这样的限制以避免我的班级人数过多。

我在那个类中有 8 或 9 个通用方法,它们几乎都以这种方式工作。直观的做法是@DanielHilgarth 建议每种类型只有一种方法。目前只能用 4 或 5 种类型调用方法。但是,这仍然意味着该类中有 32-40 个方法。

如果可能的话,我想避免这种情况。

Edit2:防止调用“真实”类的需求来自协变/逆变问题。 EntityDao 和 ValueDao Get&lt;T&gt; 方法返回 IEntityIValue 对象。当我在 GetAll&lt;T&gt; 方法中调用集合时,当我查询单个对象失败时,什么工作正常,因为我无法在 IEnumerable&lt;Value&gt; 中转换 IEnumerable&lt;IValue&gt;

我刚刚注意到来自@JonSkeets 的this answer 关于列表的转换。这可能是一种解决方法...

【问题讨论】:

能否解释一下,为什么需要这样的约束? 当然,我刚刚编辑了问题。 这是一个解释,你为什么要创建泛型方法而不是工厂方法的数量。如果你解释一下为什么这个方法应该只受接口类型的限制,那将会很有帮助。 【参考方案1】:

您应该只创建专用方法。带有if 的示例代码表明您当前的方法没有做任何事情。它有多个。

随你去吧:

GetEntity(42);
GetValue(13);

public IEntity GetEntity(long id)

    return EntityDao.Get(id);


public IValue GetValue(long id)

    return ValueDao.Get(id);

这在所有层上都更干净:

    GetEntityGet&lt;IEntity&gt; 您清楚地传达了可能的情况。您没有任何运行时异常。 您的 get 方法不需要任何类型切换。

如果这会导致你的服务上有太多类似的方法,那么是时候打破新的类了,例如一个用于Entity,一个用于Value。 然后,您可以提供返回新类的服务属性。这与我在实现my query objects 时所做的相同。然后它可能看起来像这样:service.Values.Get(13)service.Entities.Get(42)

【讨论】:

我可以,但我担心课堂上的方法“过多”。我有类似 8 或 9 个通用方法,它们在该类中都以类似的方式工作。如果我分解我的泛型方法,我会得到大约 40 种方法。如果可能的话,我想避免这种情况。 @TimBourguignon:在这种情况下,您应该开设一些新课程。 这确实是一种可能性......我一直试图避免这种可能性,因为该服务中的单个入口点确实很方便(所有提到的方法都是吸气剂,所以在某种程度上它是有意义的它们都组合在一起)... 您可以提供返回新类的服务属性。这与我在实现my query objects 时所做的相同。它可能看起来像这样:service.Values.Get(13)service.Entities.Get(42) 我不得不考虑一下,但这是个好主意,它看起来确实很性感:D【参考方案2】:

这可能是另一种选择:

abstract class Persistable<T>

    protected static Func<long, T> mapFunction;
    public static T Get(long id)
    
        return mapFunction(id);
    


class Entity : Persistable<Entity>

    public static Entity()
    
        Persistable<Entity>.mapFunction = input => EntityDao.Get(input);
    

class Value : Persistable<Value>

    public static Value()
    
        Persistable<Value>.mapFunction = input => ValueDao.Get(input);
    

您的Get 方法将是这样的:

public T Get<T>(long id) // maybe you restrict the T to something

    Persistable<T>.Get(id);

【讨论】:

以上是关于具有接口类型约束的 C# 泛型方法的主要内容,如果未能解决你的问题,请参考以下文章

C# 泛型方法约束为继承自某类时,调用方法,传子类实参,为什么报错?应该怎么写

C#进阶C# 泛型

C#泛型方法,泛型约束问题

C#泛型约束

请教一个unity有关于泛型参数的问题

《C#零基础入门之百识百例》(八十二)泛型类型参数Where约束 -- 泛型单例