2014-02-11 4 views
0

Мой вопрос похож на this question с той разницей, что вместо того, чтобы ограничивать производный класс напрямую, я хотел бы ограничить интерфейс этого класса.Как ограничить общий тип абстрактного класса интерфейсом производного класса?

Некоторые из предпосылок: Я пытаюсь создать безопасную интерфейсную структуру для создания SignalR Hub Proxies, в основном для случая, если имя узла или один из его методов изменяется, мне не нужно искать через кучу строки для решения проблемы; на этой заметке мой клиент 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); 
    } 

    ... 
} 

Как я могу удалить необходимость указать интерфейс для базового и производный? Я открыт для реструктуризации этого немного, а также не слишком далеко от общепринятого отверстия кролика.

+0

Я не думаю, что вы можете удалить необходимость указать интерфейс в обоих местах, но вы можете применять, что подклассы реализовать интерфейс, добавив еще один параметр типа для базового класса, например, 'class BaseClientProxy где T: BaseClientProxy , I где I: class {...}' – Lee

ответ

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. Это может быть приемлемым, если вы можете сказать в использовании клиента, есть ли у вас прокси (который может победить цель). Если, как вы говорите, вы все еще на этапе проектирования, возможно, вы можете настроить клиентов для соответствия этой структуре.

+0

Это то, чего я боялся. Я собираюсь исследовать с помощью функции Castle Dynamic Proxy для достижения моей цели; спасибо за разъяснение. – Phaeze

1

Если я понимаю правильно, вы хотите что-то вроде

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

Это невозможно в .NET.Даже если это было возможно, на этом уровне T не предоставляет никакой информации (в любом случае гарантированно будет Object, интерфейс, который у вас уже есть). Если вы ограничиваете T интерфейсом, это будет странно. Вы можете ограничить любой производный класс реализацией любого интерфейса, который вы хотите, указав, что вы реализуете их, и оставляете все операции интерфейса абстрактными (другими словами, у вас уже есть это ограничение).

Если вы хотите получить из BaseClient a generalFoo, где T - конкретный интерфейс, но его реализации могут отличаться, я бы пошел на агрегат. Просто добавьте свойство типа T в BaseClient, и если любой производный класс захочет ограничить T, это может сделать и использовать это свойство. Возвращенный экземпляр гарантированно реализует интерфейс, которому вы ограничиваете T.

Надеется, что это помогает

Смежные вопросы