如何将泛型类型的抽象类约束到派生类的接口?

时间:2014-02-11 17:07:48

标签: c# generics inheritance signalr

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

一些背景:我正在尝试创建一个类型安全的接口驱动框架来创建SignalR Hub代理,主要是因为如果集线器名称或其中一个方法发生更改,我不必搜索一堆字符串解决问题;在那个笔记上我的信号r客户端都是点网,所以不用担心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);
    }

    ...
}

如何删除指定基础和派生的接口的需要? 我对这一点进行重组是开放的,而且我不会在众所周知的兔子洞中走得太远。

2 个答案:

答案 0 :(得分: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的选择不受约束。如果你能告诉客户使用你是否有代理(这可能会破坏目的),这可能是可以接受的。如果,正如您所说,您仍处于设计阶段,也许您可​​以调整客户端以匹配此结构。

答案 1 :(得分:1)

如果我理解正确,你需要像

这样的东西
public abstract class BaseClient<T> : T
{ ... }

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

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

希望有所帮助