2010-04-14 10 views
8

Cóż, nie wiem, czy "silne nazewnictwo" jest właściwym terminem, ale chcę to zrobić w następujący sposób.Czy mogę używać Ninject ConstructorArguments z silnym nazewnictwem?

Obecnie używam ConstructorArgument jak np. to:

public class Ninja 
{ 
    private readonly IWeapon _weapon; 
    private readonly string _name; 

    public Ninja(string name, IWeapon weapon) 
    { 
     _weapon = weapon; 
     _name = name; 
    } 
    // ..more code.. 
} 

public void SomeFunction() 
{ 
    var kernel = new StandardKernel(); 
    kernel.Bind<IWeapon>().To<Sword>(); 
    var ninja = kernel.Get<Ninja>(new ConstructorArgument("name", "Lee")); 
} 

Teraz, jeśli zmienić nazwę parametru „name” (na przykład za pomocą ReSharper) ConstructorArgument nie zostaną zaktualizowane, a ja dostać błąd wykonania podczas tworzenia Ninja. Aby to naprawić, muszę ręcznie znaleźć wszystkie miejsca, dla których określam ten parametr za pomocą ConstructorArgument i zaktualizować go. Nic dobrego, aw pewnym momencie skazuję się na porażkę, mimo że mam dobry zasięg testów. Zmiana nazwy powinna być tanią operacją.

Czy istnieje sposób, w jaki można zamiast tego podać odniesienie do parametru - taki, że jest aktualizowany po zmianie nazwy parametru?

Odpowiedz

5

Jeśli możesz udostępnić więcej tego, co naprawdę chcesz osiągnąć, otrzymasz lepszą odpowiedź. Ogólnie rzecz biorąc, nie chcesz polegać na przekazywaniu ConstructorArgument w ogóle, jeśli można temu zaradzić - powinno to być ostatecznym sposobem uprzątnięcia wartości parametru w celu stworzenia komponentu, którego nie posiadasz, a tym samym może polegać na nie przemianowanie [jak] chcąc nie chcąc podczas refaktoryzacji. Więc jeśli chodzi o normalny kod, możesz spróbować utrzymać go w interfejsach, aby uczynić rzecz niedwuznaczną i nie polegać na nazwach, które są lepsze.

Nie możemy teraz skopiować przykładu, ale istnieje dość popularny typ o nazwie static reflection. Dostarczony ConstructorArgument może dopasować dowolny parametr o tej nazwie do dowolnego konstruktora, więc statyczne odbicie nie jest najwłaściwszą rzeczą w tym przypadku.

Stąd najlepiej, że będzie prawdopodobnie odbicie statyczne pozwalają osiągnąć coś takiego jak:

var ninja = ninject.Get<Ninja>(ParamNamesOf(()=>new Ninja("dummy", "dummy")).First()); 

Typowym przykładem zobaczysz gdzie chce się wyodrębnić nazwę właściwości są dostępne na instancja. Jest to trochę inne, ponieważ musi pracować nad wyrażeniem wywołania konstruktora.

Co do znalezienia odpowiedniej biblioteki, która już to ma, wykonaj ćwiczenie dla osoby wyszukującej: D (Sugerowałbym jednak znalezienie lepszego sposobu na wyrażenie tego, co chcesz zrobić, mimo to nie używasz ConstructorArgument zamiast tego podejścia.)

+0

Używam tego tylko z komponentami, które posiadam, więc nie zmienią się nagle, chyba że im to powiem. Ale nie chcę, aby moje ręce były związane podczas refaktoryzacji ... – stiank81

+0

Rozumiem, że jeden zdecydowanie nie chciałby, żeby twój kod był kruchy, gdybyś musiał pozwać ConstructorArgument (stąd ja + 1 pytanie). Chciałem powiedzieć, że jeśli posiadasz kod, prawdopodobnie możesz zrobić to lepiej niż opierać się na ConstructorArgument w pierwszej kolejności [podczas gdy nie posiadasz kodu, istnieją uzasadnione powody, aby tego dokonać, ale negatywne skutki zostałyby złagodzone stopień przez to, że jest mniej prawdopodobne, aby się zmienić]. –

+0

Nie martw się - Rozumiem pytanie i nie próbuję podać ci innej odpowiedzi, która nie rozwiąże problemu, który sam wymyśliłeś. (Nie zadałbym sobie trudu odpowiadania, gdyby nie fakt, że czułem, że nie słyszałeś odpowiedzi na pytanie.) –

4

Jak już zauważyłeś, podejście to jest bardzo delikatne i należy go unikać. Wypróbuj projekt, jeśli nie musisz dodawać nazwy jako argumentu konstruktora. Możesz to zrobić na przykład:

public class Ninja 
{ 
    private readonly IWeapon _weapon; 

    public Ninja(IWeapon weapon) 
    { 
     _weapon = weapon; 
    } 

    public string Name { get; set; } 

    // ..more code.. 
} 

public void SomeFunction() 
{ 
    var kernel = new StandardKernel(); 
    kernel.Bind<IWeapon>().To<Sword>(); 
    var ninja = ninject.Get<Ninja>(); 
    ninja.Name = "Lee"; 
} 

Mam nadzieję, że to pomoże.

+1

Thx za komentarz, ale nie wiem .. Zastrzyk własności również może być kruchy. Jeśli Ninja nie może być użyty bez nazwy, musisz zaufać wszystkim, aby pamiętali dodawanie imienia po utworzeniu ninja. – stiank81

+2

Niekoniecznie. Jeśli nie dodanie nazwy jest nieprawidłowe, klasa 'Ninja' powinna sprawdzić to, gdy jest używana (i inaczej podać" InvalidOperationException "). Nie zapomnij, że przy początkowym rozwiązaniu (w pytaniu) masz ten sam problem.Możesz również stworzyć metodę fabryczną, która zajmie się tym (fabryka zadzwoni do twojego kontenera). W ten sposób tworzenie i inicjowanie będzie wykonywane tylko w jednym miejscu. – Steven

+0

Oczywiście - mam problem w moim początkowym rozwiązaniu, ale nie ten sam problem. Fabryka brzmi jak dobry pomysł. Przyjrzyj się, czy może pomóc w mojej sytuacji! – stiank81

0

Tak, możesz.

Mam imlemented to:

public string GiveConstuctorArgumentName(Type class, Type constructorArgument) 
    { 
     var cons = class.GetConstructors(); 

     foreach (var constructorInfo in cons) 
     { 
      foreach (var consParameter in constructorInfo.GetParameters()) 
      { 
      if (consParameter.ParameterType == constructorArgument) 
      { 
       return consParameter.Name; 
      } 
      } 
     } 

     throw new InstanceNotFoundException(); 
    } 

Its bez LINQ, ale jest to dobry punkt startowy, aby zrozumieć, w jaki sposób jego praca.

Powiązane problemy