【问题标题】:How to constrain generic type of abstract class to interface of derived class?如何将抽象类的泛型类型约束到派生类的接口?
【发布时间】:2014-02-12 10:04:53
【问题描述】:

我的问题与this question 类似,不同之处在于我不想直接约束到派生类,而是想约束到该类的接口。

一些背景:我正在尝试创建一个类型安全的接口驱动框架来创建 SignalR 集线器代理,主要是为了如果集线器名称或其方法之一发生更改,我不必搜索一堆字符串解决问题;在那张纸条上,我的信号 r 客户端都是 dot net,所以不用担心 java 脚本。

所以如果我有这个基类:

public abstract class BaseClientProxy<T>
{
    protected IHubProxy proxy;

    protected void Invoke<TDto>(TDto dto)
    {
        proxy.Invoke(this.GetCallingMethod(), dto);
    }

    protected void Invoke()
    {
        proxy.Invoke(this.GetCallingtMethod());
    }

    protected BaseClientProxy(HubConnection conn)
    {
        proxy = conn.CreateHubProxy<T>();
    }
}

还有一个集线器代理,例如:

    public class FoobarServiceProxy : BaseClientProxy<IFoobarService>, IFoobarService
{
    public FoobarServiceProxy(HubConnection conn) : base(conn)
    {          
    }

    public void Bar(BarDto bar)
    {
        proxy.Invoke(this.GetCurrentMethod(), bar);
    }

    ...
}

我怎样才能消除为基类和派生类指定接口的需要? 我愿意对此进行一些重组,而且我还没有进入众所周知的兔子洞。

【问题讨论】:

  • 我不认为您可以消除在这两个地方指定接口的需要,但是您可以通过向基类添加另一个类型参数来强制子类实现接口,例如class BaseClientProxy&lt;T, I&gt; where T : BaseClientProxy&lt;T, I&gt;, I where I : class { ... }

标签: c# generics inheritance signalr


【解决方案1】:

看来您想要的是一种“通用代理”,您可以针对任何接口创建它。你想做这样的事情:

public abstract class BaseClientProxy<T> : T

换句话说,你想让一个类实现一个在编译时未知的接口。

这是不允许的。如果你尝试编译它,你会得到

error CS0689: Cannot derive from 'T' because it is a type parameter

这是不可能编译的,因为你不知道T 的成员会是什么。即使您在T 上设置了类型约束,允许编译器知道其一些成员,您仍然无法知道可能需要的所有成员运行时(例如添加成员的子接口)。

但是,您可以向抽象类添加类型约束,以保证T 在所有情况下都实现给定接口。不幸的是,您仍然必须对所有子类应用相同的约束,但幸运的是编译器会为您检查。

另一种选择是使代理成为父类的成员(而不是基类):

public abstract BaseClientProxy<T>
{
    public T Client {
        // etc
    }
}

您不能用代理本身代替T,但您可以不受限制地选择T。如果您可以在客户端使用中判断您是否有代理(这可能会破坏目的),这可能是可以接受的。如果如您所说,您仍处于设计阶段,也许您可​​以调整客户端以匹配此结构。

【讨论】:

  • 这就是我所担心的。我将使用 Castle Dynamic Proxy 进行调查以实现我的目标;谢谢你的澄清。
【解决方案2】:

如果我理解正确,你想要类似的东西

public abstract class BaseClient<T> : T
{ ... }

这在 .NET 中是不可能的。即使这是可能的,在那个级别上,T 也不提供任何信息(在任何情况下,它都保证是 Object,一个你已经拥有的接口)。如果您将 T 限制为接口,那将很奇怪。您可以限制任何派生类实现您想要的任何接口,方法是说您实现它们并将所有接口操作保留为抽象(换句话说,您已经有了这个约束)。

如果你想从 BaseClient 派生一个 GeneralFoo,其中 T 是一个具体的接口,但它的实现可能会有所不同,那么我会选择聚合。只需将 T 类型的属性添加到 BaseClient ,如果任何派生类想要限制 T 可以这样做并使用该属性。返回的实例保证实现你约束 T 的接口。

希望对你有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 2014-05-07
    • 1970-01-01
    • 2010-11-08
    相关资源
    最近更新 更多