2010-01-29 7 views
-1

Я пытаюсь написать код, помогающий тестировать службы WCF. Доступ к этим службам осуществляется через класс фасадов, который создает экземпляр прокси, затем вызывает метод прокси и возвращает результат; для каждого метода прокси. Я хотел бы иметь возможность заменить текущий код создания тем, что либо создает реальный сервис, либо поддельный.Общие ограничения и наследование

Я не мог заставить это работать. Я варил вниз к следующему:

using System.ServiceModel; 

namespace ExpressionTrees 
{ 
    public interface IMyContract 
    { 
     void Method(); 
    } 

    public class MyClient : ClientBase<IMyContract>, IMyContract 
    { 
     public MyClient() 
     { 
     } 

     public MyClient(string endpointConfigurationName) 
      : base(endpointConfigurationName) 
     { 
     } 

     public void Method() 
     { 
      Channel.Method(); 
     } 
    } 

    public class Test 
    { 
     public TClient MakeClient<TClient>() 
      where TClient : ClientBase<IMyContract>, IMyContract, new() 
     { 
      return new MyClient("config"); 

      // Error: 
      // Cannot convert expression of type 'ExpressionTrees.ServiceClient' to return type 'TClient' 
     } 
    } 
} 

Почему, несмотря на то, MyClient класс является производным от ClientBase<IMyContract> и реализует IMyContract, что я не могу вернуть MyClient экземпляр из метода означало вернуть TClient? TClient указывает ограничение типа, которое, как я думал, означало одно и то же.


Моя цель состояла в том, чтобы вызывать код как это:

public void CallClient<TClient>() 
     where TClient : ClientBase<IMyContract>, IMyContract 
    { 
     TClient client = null; 
     bool success = false; 
     try 
     { 
      client = MakeClient<TClient>(); 
      client.Method(); 
      client.Close(); 
      success = true; 
     } 
     finally 
     { 
      if (!success && client != null) 
      { 
       client.Abort(); 
      } 
     } 
    } 

Но вместо того, чтобы всегда называя MakeClient<TClient>, я хотел, чтобы быть в состоянии иметь тестовый модуль впрыснуть фиктивного объекта. Поскольку код выше зависит как от ClientBase<IMyContract>, IMyContract, кажется, я пытался «синтезировать» общий класс, который бы удовлетворял этому ограничению.

В ретроспективе это не имеет смысла. Например, ClientBase<IMyContract> будет создан таким образом, чтобы был создан объект Channel, который затем мог бы делегировать метод Close.

Я наткнулся на то, что тот же самый код работает как для реального, так и для поддельных сервисов. Теперь я ввожу IMyService и вызывая IMyService.Method или client.Method в зависимости от того, является ли мое введенное свойство нулевым.

+1

Я до сих пор не могу представить, почему этот вопрос привлекает вниз. Хотел бы, чтобы один из нижеперечисленных сказал мне, чтобы я мог улучшить вопрос. –

ответ

8

В основном код сводится к:

public static T MakeFruit<T>() where T : Fruit 
    { 
     return new Apple(); 
    } 

Это всегда возвращает яблоко, даже если вы звоните MakeFruit<Banana>(). Но MakeFruit<Banana>() требуется, чтобы вернуть банан, а не яблоко.

Значение ограничения общего типа заключается в том, что аргумент типа, предоставленный вызывающим, должен соответствовать ограничению. Итак, в моем примере вы можете сказать , но не MakeFruit<Tiger>(), потому что Tiger не соответствует ограничению, которое T должно быть конвертируемым в Fruit. Я думаю, вы считаете, что ограничение означает что-то другое; Я не уверен, что это.

Подумайте об этом. Формальный параметр имеет формальный тип параметра. Формальный тип параметра ограничивает тип выражения, которое используется в качестве аргумента. Так что, когда вы говорите:

void M(Fruit x) 

вы говорите «аргумент, переданный для формального параметра х в М должен быть конвертирован в Fruit».

Ограничения параметров типового типа в точности одинаковы; они являются ограничениями на то, какие аргументы типа могут быть переданы для параметров типового типа. Когда вы говорите «где T: Fruit», это то же самое, что сказать (Fruit x) в списке формальных параметров. T должен быть типом, который идет к Fruit, так же, как аргумент для x должен быть аргументом, который идет к Fruit.

Почему вы вообще хотите иметь общий метод? Я не понимаю, что именно вы пытаетесь моделировать с помощью этого метода или почему вы хотите, чтобы он был общим.

+0

Эрик, я возьму пример на один шаг глубже, поэтому ясно, что я пытаюсь сделать. –

3

Вы ограничиваете TClient в части вызова MakeClient<TClient>(), а не типом возврата.

Возвращаемый тип должен соответствовать типу родового параметра, но картина этого:

public class MyOtherClient : ClientBase<IMyContract>, IMyContract 
{ 
    public void Method() 
    { 
     Channel.Method(); 
    } 
} 

Это также действительный обратный по телефону MakeClient<MyOtherClient>, который MyClient не конвертируемая к, так как он должен возвращать тип от MyOtherClient.

Обратите внимание, что изменение возврата к:

return new MyClient() as TClient; 

, вероятно, получить его мимо компилятором, но быть пустым в моем сценарии выше во время выполнения.

0

Это должно решить вашу проблему.

static T Make<T>() where T : IConvertible 
{ 
    var s = ""; 
    return (T)(Object)s;   
} 
+0

К сожалению, нет. Мне действительно нужен параметризованный конструктор. Я обновил свой вопрос, чтобы показать это. –

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