C # несколько аргументов в одном, чтобы высушить передачу параметров - PullRequest
8 голосов
/ 12 января 2010

Я не знаю, возможно ли это, но в некоторых из моих модульных тестов я заканчивал тем, что инициализировал разные объекты с одинаковыми аргументами. Я хотел бы иметь возможность хранить эти аргументы в некоторой переменной и просто инициализировать конструктор многопараметрического объекта с этой переменной, вместо того, чтобы делать:

Thing thing1 = new Thing(arg1, arg2, arg3, arg4);
Thing thing2 = new Thing(arg1, arg2, arg3, arg4);
Thing thing3 = new Thing(arg1, arg2, arg3, arg4);

Я мог бы сделать следующее:

MagicalArgumentsContainer args = (arg1, arg2, arg3, arg4);
Thing thing1 = new Thing(args);
Thing thing2 = new Thing(args);
Thing thing3 = new Thing(args);

Есть ли способ сделать это, не переопределяя конструктор Thing, чтобы взять список, из которого он вручную разбирает и извлекает аргументы? Может быть, немного синтаксического C # сахара?

Ответы [ 7 ]

13 голосов
/ 12 января 2010

Я имею в виду, вот что:

Func<Thing> f = () => new Thing(arg1, arg2, arg3, arg4);
Thing thing1 = f();
Thing thing2 = f();
Thing thing3 = f();
Thing thing4 = f();

Только будьте осторожны с семантикой закрытия .

3 голосов
/ 12 января 2010

Ну, я думаю, вы могли бы использовать контейнер IoC, поскольку некоторые из них также предлагают ObjectFactory, т.е. вы говорите IoC, как создать новый экземпляр типа T, а затем просто просите IoC дать вам его экземпляр .

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

public MagicFactory
{
   T arg1, T2 arg2,  T3 arg3,.., TN argN;

   public MagicFactory(T1 a1,..., TN aN)
   {
      this.arg1=a1;
       ...
      this.argN = an;
   }

   public Thing GimmeDaThing()
   {
      return new Thing(this.arg1,...,this.argN);
   }
}

однако имейте в виду, что если аргументы не являются типом значения, то все ваши экземпляры Thing будут иметь ссылки на одни и те же объекты, поэтому, даже если у вас разные экземпляры вещей, они все будут указывать то же самое arg1. Чтобы исправить это, нужно на самом деле получить Func в параметре, чтобы вы могли создать новый:

public MagicFactory
{
   Func<T1> arg1, ,.., Func<TN> argN;

   public MagicFactory(Func<T1> a1,..., Func<TN> aN)
   {
      this.arg1=a1;
       ...
      this.argN = an;
   }

   public Thing GimmeDaThing()
   {
      return new Thing(this.arg1(),...,this.argN());
   }
}

и вы бы назвали это так:

var magicContainer = new MagicFactory(()=> new T1(...),..., ()=>new T2(..);


var thing1 = magicContainer.GimmeDaThing();
var thing1 = magicContainer.GimmeDaThing();
var thing1 = magicContainer.GimmeDaThing();
var thing1 = magicContainer.GimmeDaThing();

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

1 голос
/ 26 марта 2011

Используйте объявление params в вашем методе так:

public Thing(params string[] args)
{
    foreach(string s in args)
    {
        ...
    }
}

, и это позволит вам сделать следующее:

result = Things(arg1)
result = Things(arg1,arg2)
result = Things(arg1,arg2,arg3)
result = Things(arg1,arg2,arg3,arg4)
1 голос
/ 12 января 2010

Я бы посоветовал изучить шаблон Test Data Builder . Это работает очень хорошо, когда у вас есть много параметров, которые вы хотите изменить независимо, повторно использовать и так далее.

Вы можете использовать свойства + инициализаторы объектов для «плоских» классов или альтернативный метод создания цепочек методов. Я играл с обоими, и у каждого есть свои преимущества.

Преимущества:

  • Вы можете захватить переменные / значения которые были использованы для создания объекта
  • Вы можете повторно использовать экземпляр компоновщика, если значения это простые типы и / или неизменяемые (типы значений, струны и т. д.)
  • Вы можете варьировать каждый ктор параметр независимо без шума / дублирование кода делает тесты читать очень приятно, а не нужно помнить, какой параметр Ctor это то, что вы видите имя.

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

В любом случае, вот код:

public class ThingBuilder
{
   // set up defaults so that we don't need to set them unless required
   private string m_bongoName = "some name";
   private DateTime m_dateTime = new DateTime(2001, 1, 1);
   private int m_anotherArg = 5;
   private bool m_isThisIsGettingTedious = true;

   public ThingBuilder BongoName(string bongoName)
   {
      m_bongoName = bongoName;
      return this;
   }

   public ThingBuilder DateTime(DateTime dateTime)
   {
      m_dateTime = dateTime;
      return this;     
   }

   // etc. for properties 3...N

   public Thing Build()
   {    
      return new Thing(m_bongoName, m_dateTime, m_anotherArg, m_isThisGettingTedious);
   }
}

Использование (один раз):

// notice that the parameters are now explicitly named + readable!
Thingy builtInstance = new ThingBuilder()
                           .BongoName("um bongo")
                           .DateTime(DateTime.Now)
                           .GettingTedious(true)
                           .Build();

Несколько экземпляров:

var builder = new ThingBuilder()
                  .BongoName("um bongo")
                  .DateTime(DateTime.Now)
                  .GettingTedious(true);

// let's make multiple objects
Thing builtThing = builder.Build();
Thing anotherBuiltThing = builder.Build();
1 голос
/ 12 января 2010

Вы могли бы, возможно, переписать GimmieAThing во что-то вроде GimmieAThing<T>, используя немного обобщений?

public class MagicalArgumentsContainer
    {
            object[] _myParams;

            public MagicalArgumentsContainer (params object[] myParams)
            {
            _myParams = myParams;
            }

            public Thing GimmieAThing()
            {
    return new Thing(_myParams[0], _myParams[1], _myParams[2], _myParams[3]);
        }
    }
1 голос
/ 12 января 2010

Есть также это, предполагая, что ваш Thing1 является тривиальным объектом, и вам просто нужна мелкая копия:

Thing thing1 = new Thing(arg1, arg2, arg3, arg4);
Thing thing2 = (Thing)thing1.MemberwiseClone();
0 голосов
/ 12 января 2010

Вы также можете использовать массив объектов и цикл for, если вам нужно делать это много раз.

...