Jak korzystać z opcjonalnych parametrów w C #?

492

Uwaga: To pytanie zostało zadane w czasie, gdy C # nie obsługiwał jeszcze parametrów opcjonalnych (tj. Przed C # 4).

Budujemy internetowy interfejs API generowany programowo z klasy C #. Klasa ma metodę, GetFooBar(int a, int b)a API ma metodę GetFooBarprzyjmującą parametry zapytania, takie jak &a=foo &b=bar.

Klasy muszą obsługiwać parametry opcjonalne, które nie są obsługiwane w języku C #. Jakie jest najlepsze podejście?

Kalid
źródło
7
Lub poczekaj, aż unitil C # 4.0 zostanie zwolniony. Obsługiwane są parametry opcjonalne.
Micheasza

Odpowiedzi:

1053

Zaskoczony, nikt nie wspomniał o opcjonalnych parametrach C # 4.0, które działają w ten sposób:

public void SomeMethod(int a, int b = 0)
{
   //some code
}

Edycja: Wiem, że w momencie zadawania pytania C # 4.0 nie istniało. Ale to pytanie wciąż zajmuje pierwsze miejsce w Google pod względem „opcjonalnych argumentów w języku C #”, więc pomyślałem - tę odpowiedź warto tu znaleźć. Przepraszam.

Alex
źródło
58
W chwili zadawania pytania C # 4.0 nie istniało.
Forrest Marvez
91
Tak, przepraszam. Ale to pytanie wciąż zajmuje pierwsze miejsce w Google pod względem „opcjonalnych argumentów w języku C #”, więc zresztą - ta odpowiedź warto tu być :)
Alex
45
Dobry dla ciebie za dostarczanie aktualnych informacji. Najlepiej byłoby, gdyby oryginalne odpowiedzi zostały zaktualizowane o bieżące informacje, takie jak C # 4.0. Wierzę, że tak właśnie myśleli ludzie z SO, mentalność Wiki, ale wszyscy zbytnio boją się edytować odpowiedź innej osoby.
Rowan
Coś ciekawego odkryłem w WebServices, że (z pewnością w projekcie, który testuję) wszystkie wartości bool w zapytaniu są domyślnie opcjonalne. Oczywiście domyślnie mają wartość FAŁSZ.
Carlos P
4
Warto zauważyć, że opcjonalne parametry muszą być umieszczone po wszystkich nie opcjonalnych parametrach. tzn. nie możesz mieć publicznej nieważności SomeMethod (int b = 0, int a)
Andrew Meservy 27.02.19
129

Inną opcją jest użycie słowa kluczowego params

public void DoSomething(params object[] theObjects)
{
  foreach(object o in theObjects)
  {
    // Something with the Objects…
  }
}

Nazywany jak ...

DoSomething(this, that, theOther);
Tim Jarvis
źródło
57
Ta odpowiedź wyjaśnia słowo kluczowe params beautifilly w 10 liniach, MSDN nadal nie robi tego w 30. +1
User2400 8.04.2010
1
Dzięki! Zainspirowało mnie to do napisania prostej (dla mojego obecnego przypadku użycia) funkcji rejestrowania: public void log (params object[] args){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.Length; i++){ sb.Append("{"); sb.Append(i.ToString()); sb.Append("}"); sb.Append(" "); } String.Format(sb.ToString(),args).Dump(); }Przykładowe wywołanie:log("...Done,",(watch.ElapsedMilliseconds/1000).ToString(),"s");
pfonseca
czy możesz wspomnieć, czy Net 3.5 to obsługuje? oficjalni doktorzy o tym nie wspominali.
T.Todua
@ T.Todua - Nie jestem w 100% pewien, o co tu pytasz - program dotnet Framework 3.5 zawiera C # v3.0 - wersja 3.0 obsługuje słowo kluczowe params, ale nie ma parametrów opcjonalnych, które zostały wprowadzone w C # v4.0 , który został uwzględniony w programie dotnet Framework 4.0
Tim Jarvis
@ TimJarvis tak, o to prosiłem. tnx
T.Todua
77

W języku C # normalnie użyłbym wielu form tej metody:

void GetFooBar(int a) { int defaultBValue;  GetFooBar(a, defaultBValue); }
void GetFooBar(int a, int b)
{
 // whatever here
}

AKTUALIZACJA: Wspomniałem powyżej, był sposób, w jaki zrobiłem wartości domyślne z C # 2.0. Projekty, nad którymi teraz pracuję, używają C # 4.0, który teraz bezpośrednio obsługuje parametry opcjonalne. Oto przykład, którego właśnie użyłem we własnym kodzie:

public EDIDocument ApplyEDIEnvelop(EDIVanInfo sender, 
                                   EDIVanInfo receiver, 
                                   EDIDocumentInfo info,
                                   EDIDocumentType type 
                                     = new EDIDocumentType(EDIDocTypes.X12_814),
                                   bool Production = false)
{
   // My code is here
}
Stephenbayer
źródło
1
Uważam, że nie można wywoływać konstruktorów w parametrach domyślnych, muszą one być zgodne z „czasem kompilacji”, a nie „czasem wykonywania” ... A może się mylę?
Alex
45

Z tej strony:

http://www.tek-tips.com/viewthread.cfm?qid=1500861&page=1

C # pozwala na użycie atrybutu [Opcjonalnie] (z VB, choć nie działa w C #). Możesz mieć taką metodę:

using System.Runtime.InteropServices;
public void Foo(int a, int b, [Optional] int c)
{
  ...
}

W naszym opakowaniu API wykrywamy parametry opcjonalne (ParameterInfo p.IsOptional) i ustawiamy wartość domyślną. Celem jest oznaczenie parametrów jako opcjonalne bez uciekania się do klaudges, takich jak „opcjonalne” w nazwie parametru.

Kalid
źródło
4
Ale jak w takim razie użyć funkcji Foo? Foo (1,1); foo (1,1, null) i foo (1,1, Missing.value) wyrzucą wyjątki
Bolu
2
Dziwne, niektóre powyższe odpowiedzi są znacznie lepsze niż to.
Maidot
26

Możesz użyć przeciążenia metody ...

GetFooBar ()
GetFooBar (int a)
GetFooBar (int a, int b)

Zależy to od sygnatur metody, w podanym przykładzie brakuje metody „int b”, ponieważ miałaby taką samą sygnaturę jak metoda „int a”.

Możesz użyć typów dopuszczających wartości zerowe ...

GetFooBar (int? A, int? B)

Następnie możesz sprawdzić za pomocą a.HasValue, czy parametr został ustawiony.

Inną opcją byłoby użycie parametru „params”.

GetFooBar (argumenty obiektu [] argumenty)

Jeśli chcesz użyć nazwanych parametrów, musisz utworzyć typ do ich obsługi, chociaż myślę, że jest już coś takiego dla aplikacji internetowych.

Kepboy
źródło
24

Możesz używać opcjonalnych parametrów w C # 4.0 bez żadnych obaw. Jeśli mamy metodę taką jak:

int MyMetod(int param1, int param2, int param3=10, int param4=20){....}

podczas wywoływania metody można pominąć parametry takie jak to:

int variab = MyMethod(param3:50; param1:10);

C # 4.0 implementuje funkcję zwaną „nazwanymi parametrami”, możesz faktycznie przekazywać parametry według ich nazw i oczywiście możesz przekazywać parametry w dowolnej kolejności :)

kristi_io
źródło
20

Witaj, opcjonalny świecie

Jeśli środowisko wykonawcze ma podawać domyślną wartość parametru, należy użyć odbicia, aby wykonać połączenie. Nie tak ładne, jak inne sugestie dotyczące tego pytania, ale zgodne z VB.NET.

using System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace ConsoleApplication1
{
    class Class1
    {
        public static void sayHelloTo(
            [Optional,
            DefaultParameterValue("world")] string whom)
        {
            Console.WriteLine("Hello " + whom);
        }

        [STAThread]
        static void Main(string[] args)
        {
            MethodInfo mi = typeof(Class1).GetMethod("sayHelloTo");
            mi.Invoke(null, new Object[] { Missing.Value });
        }
    }
}
Hugh Allen
źródło
16

Prostym sposobem, który pozwala pominąć dowolne parametry w dowolnej pozycji , jest wykorzystanie typów zerowalnych w następujący sposób:

public void PrintValues(int? a = null, int? b = null, float? c = null, string s = "")
{
    if(a.HasValue)
        Console.Write(a);
    else
        Console.Write("-");

    if(b.HasValue)
        Console.Write(b);
    else
        Console.Write("-");

    if(c.HasValue)
        Console.Write(c);
    else
        Console.Write("-");

    if(string.IsNullOrEmpty(s)) // Different check for strings
        Console.Write(s);
    else
        Console.Write("-");
}

Ciągi są już typami zerowalnymi, więc nie potrzebują ? .

Po uzyskaniu tej metody wszystkie wywołania są prawidłowe :

PrintValues (1, 2, 2.2f);
PrintValues (1, c: 1.2f);
PrintValues(b:100);
PrintValues (c: 1.2f, s: "hello");
PrintValues();

Kiedy definiujesz metodę w ten sposób, możesz dowolnie ustawiać tylko te parametry, które chcesz nazwać . Zobacz poniższy link, aby uzyskać więcej informacji na temat nazwanych i opcjonalnych parametrów:

Argumenty nazwane i opcjonalne (Podręcznik programowania w języku C #) @ MSDN

SteakOverflow
źródło
9

Zgadzam się ze Stephenbayerem. Ponieważ jednak jest to usługa internetowa, użytkownikowi końcowemu łatwiej jest użyć tylko jednej formy metody internetowej niż wielu wersji tej samej metody. Myślę, że w tej sytuacji typy dopuszczające wartości zerowe są idealne dla parametrów opcjonalnych.

public void Foo(int a, int b, int? c)
{
  if(c.HasValue)
  {
    // do something with a,b and c
  }
  else
  {
    // do something with a and b only
  }  
}
Vivek
źródło
+1 Słowo porady. Nie rób z tego nawyku, ponieważ może być naprawdę nieuporządkowany.
mhenrixon
7

parametry opcjonalne dotyczą metod. jeśli potrzebujesz opcjonalnych argumentów dla klasy i jesteś:

  • za pomocą c # 4.0: użyj opcjonalnych argumentów w konstruktorze klasy, rozwiązanie to preferuję, ponieważ jest bliżej tego, co jest zrobione metodami, więc łatwiej je zapamiętać. oto przykład:

    class myClass
    {
        public myClass(int myInt = 1, string myString =
                               "wow, this is cool: i can have a default string")
        {
            // do something here if needed
        }
    }
  • używając wersji c # wcześniejszych niż c # 4.0: powinieneś użyć łączenia konstruktorów (używając słowa kluczowego:), gdzie prostsze konstruktory prowadzą do „głównego konstruktora”. przykład:

    class myClass
    {
        public myClass()
        {
        // this is the default constructor
        }
    
        public myClass(int myInt)
            : this(myInt, "whatever")
        {
            // do something here if needed
        }
        public myClass(string myString)
            : this(0, myString)
        {
            // do something here if needed
        }
        public myClass(int myInt, string myString)
        {
            // do something here if needed - this is the master constructor
        }
    }
baskinhu
źródło
3

Typowym sposobem, w jaki jest to obsługiwane w języku C #, jak wspomniano Stephen, jest przeciążenie metody. Tworząc wiele wersji metody z różnymi parametrami, skutecznie tworzysz parametry opcjonalne. W formularzach z mniejszą liczbą parametrów zwykle wywołujesz formę metody, a wszystkie parametry ustawiają wartości domyślne w wywołaniu tej metody.

cfbarbero
źródło
2

Możesz przeładować swoją metodę. Jedna metoda zawiera jeden parametr, GetFooBar(int a)a drugi oba parametry,GetFooBar(int a, int b)

użytkownik2933082
źródło
2

Korzystanie z przeciążeń lub przy użyciu C # 4.0 lub nowszej

 private void GetVal(string sName, int sRoll)
 {
   if (sRoll > 0)
   {
    // do some work
   }
 }

 private void GetVal(string sName)
 {
    GetVal("testing", 0);
 }

źródło
1

W przypadku większej liczby parametrów opcjonalnych można użyć jednego parametru Dictionary z metodą ContainsKey. Podoba mi się to podejście, ponieważ pozwala mi przekazywać Listę lub T indywidualnie bez konieczności tworzenia całej innej metody (fajnie, jeśli na przykład parametry mają być używane jako filtry).

Przykład (nowy słownik <ciąg, obiekt> () zostałby przekazany, jeśli nie są wymagane żadne parametry opcjonalne):

public bool Method(string ParamA, Dictionary<string,Object> AddlParams) {
    if(ParamA == "Alpha" && (AddlParams.ContainsKey("foo") || AddlParams.ContainsKey("bar"))) {
        return true;
    } else {
        return false;
    }
}
Ryan
źródło
0

Zamiast parametrów domyślnych, dlaczego nie zbudować klasy słownika z przekazanego kwerendy .. implementacja, która jest prawie identyczna ze sposobem, w jaki formularze asp.net działają z kwerendami.

tj. Request.QueryString [„a”]

To oddzieli klasę skrzydła od kodu fabrycznego / kodu płyty kotłowej.


Możesz także chcieć sprawdzić usługi sieciowe za pomocą ASP.NET . Usługi sieciowe to API generowane automatycznie przez atrybuty klas C #.

Robert Paulson
źródło
0

Trochę późno na imprezę, ale szukałem odpowiedzi na to pytanie i ostatecznie wymyśliłem inny sposób na zrobienie tego. Zadeklaruj typy danych dla opcjonalnych argumentów metody internetowej, aby były typu XmlNode. Jeśli opcjonalny arg zostanie pominięty, zostanie ustawiony na null, a jeśli jest obecny, można uzyskać wartość ciągu, wywołując arg.Value, tj.

[WebMethod]
public string Foo(string arg1, XmlNode optarg2)
{
    string arg2 = "";
    if (optarg2 != null)
    {
        arg2 = optarg2.Value;
    }
    ... etc
}

Przyzwoite w tym podejściu jest również to, że strona główna wygenerowana w .NET dla WS nadal pokazuje listę argumentów (choć tracisz przydatne pola do wprowadzania tekstu do testowania).

Ron K.
źródło
3
Czy to jest lepsze niż używanie typów zerowalnych?
Kirk Broadhurst,
0

Mam do napisania usługę internetową, która zajmuje 7 parametrów. Każda z nich jest opcjonalnym atrybutem zapytania do instrukcji SQL zapakowanej przez tę usługę internetową. Tak więc przychodzą mi na myśl dwa obejścia nie opcjonalnych parametrów ... oba dość słabe:

metoda 1 (param1, para2, para 3, para 4, para 5, para 6, para 7) metoda 1 (para 1, para 2, para 3, para 4, para 5, para 6) metoda 1 (para 1, para 2, para 3, para 4, para 5, para 7 ) ... zacznij widzieć obraz. W ten sposób leży szaleństwo. Zbyt wiele kombinacji.

Teraz dla prostszego sposobu, który wygląda niezręcznie, ale powinien działać: method1 (param1, bool useParam1, param2, bool useParam2 itp.)

Jest to jedno wywołanie metody, wymagane są wartości dla wszystkich parametrów i będzie on obsługiwał każdy przypadek wewnątrz niego. Jest również jasne, jak go używać z interfejsu.

To hack, ale zadziała.

Spanky
źródło
2
Dlatego istnieją parametry dopuszczające wartości zerowe.
Kirk Broadhurst,
0

Musiałem to zrobić w serwisie internetowym VB.Net 2.0. W końcu podałem parametry jako ciągi, a następnie przekonwertowałem je na wszystko, czego potrzebowałem. Opcjonalny parametr został określony z pustym ciągiem. Nie najczystsze rozwiązanie, ale zadziałało. Uważaj tylko, aby uchwycić wszystkie wyjątki, które mogą wystąpić.

Matt
źródło
0

Na wszelki wypadek, jeśli ktoś chce przekazać callback (lub delegate) jako parametr opcjonalny, może to zrobić w ten sposób.

Opcjonalny parametr wywołania zwrotnego:

public static bool IsOnlyOneElement(this IList lst, Action callbackOnTrue = (Action)((null)), Action callbackOnFalse = (Action)((null)))
{
    var isOnlyOne = lst.Count == 1;
    if (isOnlyOne && callbackOnTrue != null) callbackOnTrue();
    if (!isOnlyOne && callbackOnFalse != null) callbackOnFalse();
    return isOnlyOne;
}
CodeArtist
źródło
0

parametry opcjonalne są niczym innym jak parametrami domyślnymi! sugeruję, aby podać oba parametry domyślne. GetFooBar (int a = 0, int b = 0), jeśli nie masz żadnej przeciążonej metody, spowoduje a = 0, b = 0, jeśli nie podasz żadnych wartości, jeśli podasz 1 wartość, spowoduje , przekazał wartość dla a, 0, a jeśli przekażesz 2 wartości, 1. zostanie przypisane do a, a drugie do b.

mam nadzieję, że odpowiada na twoje pytanie.

użytkownik3555836
źródło
0

W przypadku, gdy wartości domyślne nie są dostępne, sposobem dodania parametru opcjonalnego jest użycie klasy .NET OptionalAttribute - https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.optionalattribute ? widok = netframework-4.8

Przykład kodu znajduje się poniżej:

namespace OptionalParameterWithOptionalAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            //Calling the helper method Hello only with required parameters
            Hello("Vardenis", "Pavardenis");
            //Calling the helper method Hello with required and optional parameters
            Hello("Vardenis", "Pavardenis", "Palanga");
        }
        public static void Hello(string firstName, string secondName, 
            [System.Runtime.InteropServices.OptionalAttribute] string  fromCity)
        {
            string result = firstName + " " + secondName;
            if (fromCity != null)
            {
                result += " from " + fromCity;
            }
            Console.WriteLine("Hello " + result);
        }

    }
}
Sharunas Bielskis
źródło
-4

Możesz tego także spróbować
Typ 1
public void YourMethod(int a=0, int b = 0) { //some code }


Wpisz 2
public void YourMethod(int? a, int? b) { //some code }

Ankit Panwar
źródło