2012-07-02 13 views
27

C# 4 wprowadzono funkcję o nazwie named arguments co jest szczególnie przydatne w sytuacjach jakZmuszanie nazwane argumenty w C#

int RegisterUser(string nameFirst, string nameLast, string nameMiddle, string email) 

Czy istnieje sposób, aby wymusić za pomocą nazwanych argumentów? Może jakiś atrybut do zastosowania do metody lub przełącznika kompilatora, którego nie jestem świadomy? Myślę, że można to zrobić za pomocą narzędzi inspektora kodu, ale chcę tylko wiedzieć, czy jest inny sposób.

p.s.

Dla zainteresowanych, dlaczego ktoś może go potrzebować i dlaczego nie wystarczy użyć klasy/struktury do wykorzystania object initializers istnieją scenariusze, gdy jest to niemożliwe. Podobnie jak w przypadku połączeń z bibliotekami, które nie są kontrolowane lub nie są zgodne z dziwnymi kodami, musisz się do nich stosować.

+4

nie widzę dużo użycie w nazwie args chyba twoje argumenty są przeważnie opcjonalne. –

+1

@BobKaufman Zgaduję, że jest odwrotnie. Podczas kompilacji kodu "zapomina", czy został określony jako parametr pozycyjny, nazwany lub domyślny. –

+1

Dlaczego Ty tego potrzebujesz? Czy narzucono ci konwencję kodową? – driis

Odpowiedz

24

Nie, nie w języku C#. Zawsze będzie akceptował parametry pozycyjne, jeśli wszystkie parametry zostaną dostarczone.

Możesz wymusić build a custom FxCop rule lub StyleCop rule, aby to wymusić - jak podkreślono w komentarzach, jest to prawdopodobnie reguła StyleCop, którą chciałbyś (dzięki Krisowi).

+7

Nie można utworzyć niestandardowej reguły FxCop, ponieważ reguły FxCop działają na plikach binarnych, a nie źródłowych, a nazwane argumenty są "kompilowane". Przypuszczam, że mógłbyś stworzyć niestandardową regułę StyleCop. –

+0

@KrisVandermotten, myślę, że masz rację, jednak FxCop może wydobywać informacje z PDB, ale nie wiem, czy użycie nazwanych parametrów trafia do WPB. Jeśli tak nie jest, domyślam się, że zamiast tego potrzebna jest reguła stylu policjanta. – driis

+2

Nie, tak jak w przypadku parametrów nazwanych to PDB: "computer says no!". –

15

Możliwe jest zmuszanie dzwoniących do używania zawsze nazwanych argumentów. Nie zrobiłbym tego w większości przypadków, ponieważ jest to dość brzydkie, ale zależy to od tego, jak bardzo bezpieczne jest użycie metody.

Tutaj rozwiązanie:

int RegisterUser(
#if DEBUG 
     int _ = 0, 
#endif 
     string nameFirst = null, 
     string nameLast = null, 
     string nameMiddle = null, 
     string email = null) { /*...*/ } 

Pierwszym parametrem jest obojętne, które nie powinny być stosowane (i zestawiane daleko w wersji dla zwiększenia wydajności). Zapewnia jednak, że wszystkie następujące parametry muszą być nazwane.

Ważny jest użycie dowolnej kombinacji wymienionych parametrów:

RegisterUser(); 
    RegisterUser(nameFirst: "Joe"); 
    RegisterUser(nameFirst: "Joe", nameLast: "Smith"); 
    RegisterUser(email: "[email protected]"); 

Podczas próby użycia parametrów pozycyjnych, kod nie zostanie skompilowany.

+2

Za pomocą dyrektywy preprocesora kompilatora zachowuje się tak, jakby 'int _ = 0' nie istniał nawet, jeśli symbol DEBUG nie jest zdefiniowany, więc nie ma żadnego wpływu na kompilację wydania ... I don ' t myślisz, że ta odpowiedź robi to, co myślisz, że to robi ... – Zack

+2

Efekt jest taki, że nieużywany parametr "_" z wartością domyślną wymusza na pozostałych parametrach wartości domyślne i jest nazwany podczas wywoływania. – user4698855

+5

Brzydki, ale sprytny. – joelsand

2

Szukałem również sposobu na wymuszenie nazwanych argumentów. Parametry opcjonalne mogą być niebezpieczne, zwłaszcza jeśli masz wiele parametrów tego samego typu. Przeciążenia są prawie zawsze bezpieczniejszym rozwiązaniem, ale zdarzają się sytuacje, w których istnieje metoda, która może przyjmować wiele kombinacji argumentów, więc tworzenie 20 przeciążeń w celu uwzględnienia możliwości jest zawsze przesadą.

W sytuacjach ekstremalnych, gdzie niezwykle ważne jest, aby argumenty były nazywane przez cały czas, utworzę klasę argumentów bez zdefiniowanego konstruktora. W twoim przypadku, można to zrobić:

public class UserRegistrationArguments 
{ 
    public string nameFirst { get; set; } 
    public string nameLast { get; set; } 
    public string nameMiddle { get; set; } 
    public string email { get; set; } 
} 

nazwać to tak:

RegisterUser(new UserRegistrationArguments { nameFirst = "Bob", nameLast = "Slob" }); 

Można również uprościć to tak:

public class UserRegistrationArguments 
{ 
    public string nameMiddle { get; set; } 
    public string email { get; set; } 
} 

int RegisterUser(string nameFirst, string nameLast, UserRegistrationArguments args = null) 

... i to zrobić:

RegisterUser("Bob", "Slob", new UserRegistrationArguments { nameMiddle = "Teh" }); 

W ten sposób masz tylko jeden opcjonalny parametr er i to jest dla twoich opcjonalnych parametrów.

Edytuj: Może nie czytałem poprawnie PO. Nie używasz opcjonalnych argumentów? Jeśli nie, ta odpowiedź prawdopodobnie nie pomoże.

0

Używam innej metody. W moim setupie mam 1 parametr, którego zawsze oczekuję, a następnie kilka ciągów opcjonalnych, które naprawdę chcę mieć pewność, że użytkownik aktywnie wybrał. Tak więc mój pierwszy ciąg na tej liście jest wartością "pułapki", która jeśli zostanie ustawiona, powoduje błąd. Tak:

public HtmlString Toolbar(DynamicEntity target = null, string dontRelyOnParameterOrder = Constants.RandomProtectionParameter, string actions = null, string contentType = null, object prefill = null) 
    { 
     if (!Enabled) return null; 
     protectAgainstMissingParameterNames(dontRelyOnParameterOrder); 

     var toolbar = new ItemToolbar(target, actions, contentType, prefill); 

     return new HtmlString(toolbar.Toolbar); 
    } 

    private void protectAgainstMissingParameterNames(string criticalParameter) 
    { 
     if(criticalParameter != Constants.RandomProtectionParameter) 
      throw new Exception("when using the toolbar command, please use named parameters - otherwise you are relying on the parameter order staying the same."); 

    } 

hope you like it :)