【问题标题】:C# multiple arguments in one to DRY out parameter-passingC# 多参数合二为一干出参数传递
【发布时间】:2011-01-03 22:27:53
【问题描述】:

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

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

【问题讨论】:

  • Python的参数解包是福!
  • 我确实想,如果我在这个项目中使用 Ruby,我就不必问这个问题了。

标签: c# parameters arguments syntactic-sugar


【解决方案1】:

我的意思是,有这个:

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

请注意closure semantics。

【讨论】:

  • 是的,这就像一个 InLine tiny ObjectFactory,但要考虑到如果 arg1-arg4 是引用类型,那么你所有的东西都会共享这些对象,如果它们是值类型或者你不在乎它们是否共享,那么这可能是最简单的
【解决方案2】:

好吧,我猜你可以使用 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 实例都将引用相同的对象,因此,即使您有不同的 Things 实例,它们都将指向相同的 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 实例,每个实例都有自己的属性对象。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      您也许可以使用一些泛型将GimmieAThing 重写为GimmieAThing&lt;T&gt; 之类的东西?

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

      【讨论】:

        【解决方案5】:

        我建议研究Test Data Builder 模式。当您有许多希望独立改变、重复使用等参数时,它的效果非常好。

        您可以使用properties + object initializers for 'flat' classes 或流式方法链接作为替代方案。两者我都玩过,各有各的优点。

        好处:

        • 您可以捕获变量/值 用于构造对象
        • 您可以重复使用构建器实例,如果 它采用的值是简单类型 和/或不可变的(值类型, 字符串等)
        • 您可以改变每个 ctor 参数独立无噪声 /代码重复它使测试 读起来真的很好,而不是 必须记住哪个 ctor 参数 是哪个,你看名字。

        如果您需要为每个参数创建新实例,请查看 bangoker 的答案。

        不管怎样,这里有一些代码:

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

        【讨论】:

          【解决方案6】:

          在您的方法中使用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)
          

          【讨论】:

            【解决方案7】:

            如果您需要多次执行此操作,也可以使用对象数组和 for 循环。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-04-05
              • 2016-06-28
              • 1970-01-01
              • 2012-08-13
              • 1970-01-01
              • 2010-12-12
              相关资源
              最近更新 更多