C # - Параметры типа в конструкторе - нет универсальных - PullRequest
3 голосов
/ 05 декабря 2009

У меня есть класс, на котором я пытаюсь провести модульные тесты. Этот класс является классом обслуживания WCF. (Сделать его классом дженериков не моя цель.)

У меня есть тип уровня доступа к данным (DAL) (который называется UserDAL), который создается во многих методах. Чтобы протестировать эти методы, мне нужно проверить эти локальные переменные. (В каждом экземпляре UserDAL есть значение, специфичное для метода, поэтому изменение его на уровне класса приведет к грязному коду, поэтому я бы не стал этого делать.)

Я думаю, что было бы неплохо перегрузить конструктор и передать тип для использования в локальных методах. Пустой конструктор param все равно будет создавать обычный UserDAL, но перегруженный будет иметь фиктивный тип, реализующий IUserDAL.

Я не уверен в синтаксисе, чтобы сказать, что я хочу передать тип. Обратите внимание, что я пытаюсь передать не переменную, а тип.

Пример:

public class MyWCFClass: IMyWCFClass
{
    private TypeParam _myUserDALType;
    public MyWCFClass()
    {
        _myUserDALType = UserDAL;
    }
    public MyWCFClass(TypeParam myUserDALType)
    {
        _myUserDALType = myUserDALType;
    }

    //methods to use it
    public MyMethod()
    {
        IUserDAL userDAL = new _myUserDALType();
        //Call method in IUserDAL
        userDAL.CreateUser();
    }


    // Several similar methods that all need a different UserDAL go here
    .....
}

Итак, я не знаю, что это за тип TypeParam (я это придумал) или возможно ли вообще такое мышление.

Если у вас есть решение, не являющееся универсальным, это было бы замечательно.

Ответы [ 8 ]

8 голосов
/ 05 декабря 2009

То, что вы действительно ищете, это внедрение зависимостей, но вы можете сделать это, передав аргумент Type, а затем используя Activator.CreateInstance (Type), чтобы создать объект, когда вам это нужно.

Что касается реального DI (который значительно облегчит проведение этого тестирования), я знаю, что Spring.Net работает достаточно хорошо.

5 голосов
/ 05 декабря 2009

Вы имеете в виду Type, используя Activator.CreateInstance для создания экземпляров:

public class MyWCFClass: IMyWCFClass
{
    private Type _myUserDALType;
    public MyWCFClass()
    {
        _myUserDALType = typeof(UserDAL);
    }
    public MyWCFClass(Type myUserDALType)
    {
        _myUserDALType = myUserDALType;
    }

    //methods to use it
    public void MyMethod()
    {
        IUserDAL userDAL = (IUserDAL) Activator.CreateInstance(_myUserDALType );
        //Call method in IUserDAL
        userDAL.CreateUser();
    }
}
5 голосов
/ 05 декабря 2009

Используйте тип и используйте Activator.CreateInstance для его создания:

private Type _myUserDALType;

IUserDAL userDAL = Activator.CreateInstance(_myUserDALType) as IUserDAL;
3 голосов
/ 05 декабря 2009

Ваша настоящая проблема не в дженериках или их отсутствии. Ваша настоящая проблема в том, что MyWFCClass вызывает и new, и метод. Согласно Misko Hevery , вы получаете лучшую тестируемость, отделяя классы, которые вызывают new, от классов, которые реализуют логику. Вместо того, чтобы MyWFCClass каким-то образом знать тип, который вы хотите реализовать, и с помощью отражения, просто передайте объект IUserDal конструктору, позволяя тестовому жгуту проходить в фиктивном объекте при необходимости.

Если по какой-то причине вы не можете сделать это и не можете использовать дженерики, то вы должны сделать это сами. Передайте объект Type в конструктор MyWFCClass, затем используйте отражение, чтобы найти и вызвать нужный конструктор.

2 голосов
/ 05 декабря 2009

Если вы хотите передать тип, вы можете использовать объект Type:

public class A
{
  public A(Type classType)
  {
    object myObject = Activator.CreateInstance(...classType...);
  }
}

public class B
{
...
}

public class C
{
  public static void main(string[] args)
  {
    A a = new A(typeof(B));
  }

}

1 голос
/ 05 декабря 2009

Гораздо проще и более совместимо с другими приложениями, которые имеют эту проблему, было бы извлечь интерфейс на UserDal, тогда у вас будет что-то более похожее на:

public MyWCFClass() : this(new UserDAL())
{
}
public MyWCFClass(IUserDal userDAL)
{
    _myUserDAL = myUserDAL;
}

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

(отредактировано для уточнения альтернативного решения на основе других комментариев)

Если ваш DAL по существу бесполезен после использования, потому что он мутировал, вместо этого возьмите конструктор с IUserDalFactory, с одним методом Create().

1 голос
/ 05 декабря 2009

Если IUserDAL определяет интерфейс, который необходим вашей службе WCF для выполнения своей работы, почему бы просто не взять его экземпляр в качестве параметра конструктора? А поскольку WCF требует конструктора по умолчанию, почему бы не сделать так, чтобы этот конструктор по умолчанию вызывал ваш параметризованный конструктор с реализацией по умолчанию?

public class MyWCFClass : IMyWCFClass
{
    private readonly IUserDAL _userDAL;

    public MyWCFClass()
        : this(new DefaultUserDAL())
    {
    }

    public MyWCFClass(IUserDAL userDAL)
    {
        _userDAL = userDAL;
    }
}

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

public MyWCFClass()
    this(Container.Instance.Resolve<IUserDAL>())
{
}

При таком подходе у вашего класса WCF есть все, что ему нужно для выполнения своей работы, но он все еще тестируется модулем. Более того, он не несет ответственности за создание своих зависимостей, и это хорошо.

0 голосов
/ 05 декабря 2009

В C # есть тип, называемый «Тип». С его помощью вы можете создать параметр и передать любой допустимый тип.

private void MyMethod(Type myType)
{
  //Do something
}
...