8

我不知道这是否可能,但在我的一些单元测试中,我最终使用相同的参数初始化不同的对象。我希望能够将这些参数存储在某个变量中,并使用该变量初始化多参数对象构造函数,而不是这样做:

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# 语法糖?

4

7 回答 7

13

我的意思是,有这个:

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

请注意闭包语义

于 2010-01-11T21:10:53.747 回答
3

Well I guess you could use an IoC container, since several of this also offer an ObjectFactory, ie you tell the IoC how to make a new instance of type T and then you just ask the IoC to give you an instance of it.

However if you dont want to get an IoC, you could make yourself a little factory class

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);
   }
}

however keep in mind that if the arguments are not of value type, then all your instances of Thing will have references to the same objects, so, even though you have different instance of Things, they all would point to the same arg1. What you could do to fix that is to actually take in a Func in the parameter, so you can actually create a new one:

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());
   }
}

and you would call it like this:

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


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

and you would get a fresh instance of Thing each time, each with their own property objects.

于 2010-01-11T21:52:35.280 回答
1

还有这个,假设你的 Thing1 是一个微不足道的对象,你只需要一个浅拷贝:

Thing thing1 = new Thing(arg1, arg2, arg3, arg4);
Thing thing2 = (Thing)thing1.MemberwiseClone();
于 2010-01-11T21:13:34.257 回答
1

You could maybe rewrite GimmieAThing to something like GimmieAThing<T> using a bit of generics?

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]);
        }
    }
于 2010-01-11T21:22:09.620 回答
1

I'd suggest looking into the Test Data Builder pattern. It works really well when you have many parameters you wish to vary independently, re-use and so on.

You can use properties + object initializers for 'flat' classes, or fluid method chaining as an alternative. I've toyed with both and each has its advantages.

Benefits:

  • You can capture the variables/values that were used to construct an object
  • You can re-use a builder instance if the values it takes are simple types and/or immutable (value types, strings etc.)
  • You can vary each ctor parameter independently without noise / code duplication It makes the tests read really nicely as, instead of having to remember which ctor param is which, you see the name.

If you need to create new instances of each parameter, check out bangoker's answer.

Anyway, here's some code:

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);
   }
}

Usage (once instance):

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

Multiple Instances:

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();
于 2010-01-11T22:36:15.287 回答
1

Use the params declaration in your method like so:

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

and it will allow you to do the following:

result = Things(arg1)
result = Things(arg1,arg2)
result = Things(arg1,arg2,arg3)
result = Things(arg1,arg2,arg3,arg4)
于 2011-03-26T08:13:46.387 回答
0

You could also use an array of objects and a for loop if you need to do this many times.

于 2010-01-11T21:18:11.920 回答