2012-07-02 21 views
27

C# 4 eingeführt, um eine Funktion namens named arguments, die in Szenarien besonders nützlich ist, wieErzwingen benannte Argumente in C#

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

Gibt es eine Möglichkeit mit benannten Argumenten zu zwingen? Vielleicht ein Attribut, das man auf eine Methode oder einen Compiler-Schalter anwenden kann? Ich denke, es kann mit Code-Inspector-Tools gemacht werden, aber ich will nur wissen, ob es anders geht.

p.s.

Für diejenigen interessiert, warum man es brauchen kann und warum nicht einfach eine Klasse/Struktur verwenden object initializers gibt es Szenarien, wenn es unmöglich ist. Wie Aufrufe an Bibliotheken, die nicht in Ihrer Kontrolle sind, oder seltsame Code-Konventionen, die Sie befolgen müssen.

+4

Ich habe nicht viel Gebrauch in dem Namen args sehen, es sei denn Ihr args meist optional sind. –

+1

@BobKaufman Ich würde das Gegenteil vermuten. Wenn Sie den Code kompilieren, "vergisst" er, ob er als Positionsparameter, als Name oder als Standardwert bezeichnet wurde. –

+1

Warum brauchen Sie das? Wird Ihnen eine Code-Konvention auferlegt? – driis

Antwort

24

Nein, nicht in der Sprache C#. Es akzeptiert immer Positionsparameter, wenn alle Parameter geliefert werden.

build a custom FxCop rule oder StyleCop rule, um dies zu erzwingen - wie in den Kommentaren hervorgehoben, ist es wahrscheinlich eine StyleCop-Regel, die Sie interessieren würden (danke an Kris).

+7

Sie können keine benutzerdefinierte FxCop-Regel erstellen, da FxCop-Regeln für Binärdateien und nicht für Quelle verwendet werden und die benannten Argumente "kompiliert" sind. Ich nehme an, Sie könnten eine benutzerdefinierte StyleCop-Regel erstellen. –

+0

@KrisVandermotten, ich denke, du hast recht, aber FxCop kann Informationen aus der PDB extrahieren, aber ich weiß nicht, ob die Verwendung von benannten Parametern in die PDB geht. Wenn das nicht der Fall ist, denke ich, dass stattdessen eine Stil-Cop-Regel benötigt wird. – driis

+2

Nein, wie für benannte Parameter ist die PDB: "Computer sagt nein!". –

15

Es ist möglich, die Anrufer zu zwingen, immer benannte Args zu verwenden. Ich würde das in den meisten Fällen nicht tun, weil es ziemlich hässlich ist, aber es hängt davon ab, wie dringend eine sichere Methodenbenutzung benötigt wird. Hier

ist die Lösung:

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

Der erste Parameter ist ein Dummy, der nicht verwendet werden soll (und wird entfernt für die Effizienz in Release kompiliert). Es stellt jedoch sicher, dass alle folgenden Parameter benannt werden müssen.

Valid Nutzung ist jede Kombination der genannten Parameter:

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

Bei dem Versuch, Positionsparameter zu verwenden, wird der Code nicht kompilieren.

+2

Mit Ihrer Compiler-Präprozessordirektive verhält es sich so, als ob das 'int _ = 0' nicht existiert, wenn das DEBUG-Symbol nicht definiert ist, so dass es für einen Release-Build keinerlei Auswirkungen hat ... I don ' Ich denke, diese Antwort tut, was Sie denken, dass es tut ... – Zack

+2

Der Effekt ist, dass der unbenutzte "_" -Parameter mit Default-Wert den Rest der folgenden Parameter zu Standardwerten zwingt und beim Aufruf benannt werden. – user4698855

+5

Hässlich, aber clever. – joelsand

2

Ich habe auch eine Möglichkeit gesucht, benannte Argumente zu erzwingen. Optionale Parameter können gefährlich sein, insbesondere wenn Sie mehrere Parameter desselben Typs haben. Überladungen sind fast immer eine sicherere Lösung, aber es gibt Zeiten, in denen Sie eine Methode haben, die viele Argumente kombinieren kann, also 20 Überladungen zu erstellen, um für immer die Möglichkeit zu berücksichtigen, ist Overkill.

In Extremsituationen, in denen es von äußerster Wichtigkeit ist, Argumente immer zu benennen, erstelle ich eine Argumentklasse ohne definierten Konstruktor. In Ihrem Fall könnten Sie dies tun:

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

Nennen Sie es wie folgt aus:

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

Sie auch es so vereinfachen könnte:

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

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

... und dies tun:

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

Auf diese Weise haben Sie nur einen optionalen Parameter er und das ist für Ihre optionalen Parameter.

Edit: Vielleicht habe ich das OP nicht richtig gelesen. Sie verwenden keine optionalen Argumente? Wenn nicht, dann hilft Ihnen diese Antwort wahrscheinlich nicht.

0

Ich verwende eine andere Methode. In meinem Setup habe ich 1 Parameter, den ich immer erwarte, dann kommen eine Reihe von optionalen Strings, die ich wirklich sicher sein möchte, dass der Benutzer aktiv wählte. Also meine erste Zeichenfolge in dieser Liste ist ein "Trap" -Wert, der, wenn gesetzt, einen Fehler auslöst. Wie folgt aus:

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 :)