2010-01-11 12 views
8

Nie wiem, czy jest to możliwe, ale w niektórych moich testach jednostkowych kończę inicjowanie różnych obiektów z tymi samymi argumentami. Chciałbym być w stanie przechowywać te argumenty w jakiejś zmiennej i po prostu zainicjować obiektu konstruktora Wieloparametrowa z tej zmiennej więc zamiast robić:C# wiele argumentów w jednym do DRY out przepuszczanie parametrów

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

mogłem wykonać następujące czynności:

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

Czy jest jakiś sposób na wykonanie tej operacji bez przesłonięcia konstruktora, który wykonałby listę, która ręcznie wybucha i zrzuca argumenty? Może jakiś syntaktyczny cukier C#?

+1

Nie chcesz użyć Ruby =). –

+2

Rozpakowywanie argumentów Pythona jest błogosławieństwem! –

+1

Ja pomyślałem sobie, że gdybym używał Ruby w tym projekcie, nie musiałbym zadawać tego pytania. –

Odpowiedz

13

To znaczy, to jest:

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

Wystarczy być ostrożnym z closure semantics.

+1

tak, to jak InFine tiny ObjectFactory, ale weź pod uwagę, że jeśli arg1-arg4 są typami referencyjnymi, to wszystkie twoje rzeczy będą się dzielić tymi obiektami, jeśli są one typem wartości lub nie obchodzi cię, czy są one wspólne, to prawdopodobnie najłatwiejszy –

+0

W moim przypadku arg1-4 są łańcuchami, więc wszystko jest w porządku. –

1

Jest też to, zakładając, że Thing1 jest trywialny przedmiot, a wystarczy płytkie kopię:

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

Można także użyć tablicę obiektów i pętli for, jeśli trzeba to zrobić wiele czasy.

1

Może można przepisać GimmieAThing na coś podobnego do GimmieAThing<T> używając trochę generycznych?

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

Dobrze Myślę, że można użyć kontenera IoC, od kilku to również oferują ObjectFactory, czyli powiedzieć MKOl jak zrobić nową instancję typu T, a następnie po prostu poprosić MKOl dać jego przykład.

Jednakże, jeśli nie chcesz, aby uzyskać IoC, można zrobić sobie trochę klasy fabrycznej

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

jednak pamiętać, że jeśli argumenty nie są typu wartości, to wszystkie instancje Thing będzie mają odniesienia do tych samych obiektów, więc nawet jeśli masz różne przypadki Rzeczy, wszystkie wskazywałyby na ten sam argument1. Co można zrobić, aby naprawić to rzeczywiście wziąć w Func w parametrze, więc rzeczywiście można tworzyć nowe:

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

i byłoby to nazwać tak:

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


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

i otrzymasz za każdym razem świeże wystąpienie Rzeczy, każde z własnymi obiektami własności.

+1

public MagicFactory ? – gingerbreadboy

1

Proponuję przejrzeć wzór Test Data Builder. Działa bardzo dobrze, gdy masz wiele parametrów, które chcesz niezależnie zmieniać, używać ponownie i tak dalej.

Można użyć properties + object initializers for 'flat' classes lub alternatywnego łączenia łańcuchowego metodą płynową. Bawiłem się obydwoma i każdy ma swoje zalety.

Korzyści:

  • Można uchwycić zmienne/wartości które zostały wykorzystane do skonstruowania obiektu
  • Możesz ponownie użyć instancji budowniczy jeśli wartości to bierze to proste typy i/lub niezmienny (typy wartości, ciągi znaków itp.)
  • Możesz zmieniać każdy parametr ctor niezależnie, bez hałasu /duplikacja kodu To sprawia, że ​​testy czyta się bardzo ładnie, ponieważ zamiast trzeba pamiętać, który ctor param jest, który widzisz nazwę.

Jeśli potrzebujesz utworzyć nowe instancje każdego parametru, sprawdź odpowiedź Bangokera.

W każdym razie, oto niektóre kod:

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 (raz instancji):

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

wiele wystąpień:

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

Użyj deklaracja params w swojej metodzie tak:

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

i pozwoli Ci wykonać następujące czynności:

result = Things(arg1) 
result = Things(arg1,arg2) 
result = Things(arg1,arg2,arg3) 
result = Things(arg1,arg2,arg3,arg4)