C # 4.0 opcjonalne argumenty out / ref

212

Czy C # 4.0 pozwala na opcjonalne outlub refargumenty?

Joe Daley
źródło
2
No cóż, C ++ ma je efektywnie dla parametrów „out” - można zainicjować argument adresu na wartość null i dość często pisać kod biblioteki, który zapełni taką strukturę zwrotną tylko wtedy, gdy wskaźnik nie jest pusty. To jest idiom wracający do używania null dla „opcjonalnych argumentów” w API C.
Andy Dent
53
@Ed i wszyscy: dlaczego to nie ma sensu? Jeśli funkcja „zwraca” wartość poprzez „out”, nie chcę być zmuszany do jej zaakceptowania. Teraz wiem, że ze względów technicznych kompilator wciąż musi coś przekazać, ale nie ma powodu, dla którego nie mógł po prostu stworzyć dla mnie dumnego lokalnego.
Roman Starkov,
5
Być może nie ma to sensu z punktu widzenia tego, jak rzeczy są implementowane lub jaki faktycznie jest parametr opcjonalny. Ale jak powiedział romkyns, byłoby naprawdę miło mieć „opcjonalne argumenty wyjściowe” - parsuj to w języku angielskim zamiast CLR, a staje się to rozsądne i pożądane w IMO.
Adam Tolley,
9
C # nie, ale VB.NET tak.
Jason
5
Zostało to pobite na śmierć, jednak nie mogę nie wspomnieć o moim wsparciu dla opcjonalnych argumentów wyjściowych. Przyzwyczaiłem się do opcjonalnych argumentów poprzez odniesienie poprzez ustawienie wartości nulldomyślnej ( pochodzę z PHP ) i testowanie, nullaby kontynuować wypełnianie argumentu ( dla osób dobrze znanych, myślępreg_match() ) W każdym razie, chociaż rozumiem z technicznego punktu widzenia, może to być niemożliwe, a PHP i C # są raczej nieporównywalne, nadal byłoby to „ miłe ” narzędzie, które byłoby dostępne.
Dan Lugg

Odpowiedzi:

93

Jak już wspomniano, jest to po prostu niedozwolone i myślę, że ma to bardzo dobry sens. Jednak, aby dodać więcej szczegółów, oto cytat ze specyfikacji C # 4.0 , sekcja 21.1:

Formalne parametry konstruktorów, metod, indeksatorów i typów delegatów można uznać za opcjonalne:

fixed-parametr:
    atrybuty opt parametr modyfikator typ opcji identyfikator default-argument opt
default-argument:
    = wyrażenie

  • Stałym parametrem z default-argumentem jest opcjonalny parametr , natomiast stałym parametrem bez default-argumentem jest parametrem wymaganym .
  • Wymagany parametr nie może pojawić się po parametrze opcjonalnym na liście parametrów formalnych .
  • A reflub outparametr nie może mieć domyślnego argumentu .
Tomas Petricek
źródło
Alternatywnie możesz utworzyć przeciążenie za pomocą parametru ref / out. Tak, będziesz mieć dwie definicje funkcji, ale spełni to, czego szukasz
Czad
201

Nie.

Obejściem tego problemu jest przeładowanie inną metodą, która nie ma parametrów out / ref i która po prostu wywołuje twoją bieżącą metodę.

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

Aktualizacja: jeśli masz C # 7.0 , możesz uprościć:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(Dzięki @Oskar / @Reiner za zwrócenie na to uwagi.)

Dunc
źródło
6
jakiś pomysł na bardziej eleganckie rozwiązanie niż deklaracja temp / manekina?
Louis Rhys,
20
Co w tym nie jest eleganckiego? Wygląda mi całkiem przyzwoicie.
Neutrino,
27
Może przyzwoity, ale elegancki jest zdecydowanie w innej lidze. Fakt, że nie ma lepszego rozwiązania, nie oznacza, że ​​jest to najnowocześniejszy dekret.
o0 ”.
7
Przytrzymaj @ o0 '. W języku C # 7.0 będzie w stanie to zrobić: return SomeMethod(out string temp). Zobacz więcej tutaj: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar
7
W C # 7.0 możesz użyć tymczasowej zmiennej tylko do zapisu, takiej jak:return SomeMethod(out _);
Reiner
64

Nie, ale inną świetną alternatywą jest użycie metody z ogólną klasą szablonów dla parametrów opcjonalnych w następujący sposób:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

Następnie możesz użyć go w następujący sposób:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}
Robin R.
źródło
19
Jest to bardzo rozsądne rozwiązanie, ale należy pamiętać, że kompilator nie będzie wymuszał wymogu przypisania parametru out przed wyjściem z metody.
Ken Smith
2
Podoba mi się, ale jeśli nie chcesz tworzyć nowej klasy, możesz ją zasymulować, przekazując tablicę z jednym elementem.
zumalifeguard
30

Istnieje sposób na zrobienie tego, który jest dozwolony przez C #. To wraca do C ++ i raczej narusza ładną, obiektową strukturę C #.

ZACHOWAJ TĘ METODĘ Z UWAGĄ!

Oto sposób, w jaki deklarujesz i piszesz swoją funkcję z opcjonalnym parametrem:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

Następnie wywołaj funkcję w następujący sposób:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

Aby to skompilować, musisz włączyć niebezpieczny kod w opcjach projektu. Jest to naprawdę hacking rozwiązanie, które zwykle nie powinno być stosowane, ale jeśli potrzebujesz jakiejś dziwnej, tajemniczej, tajemniczej decyzji inspirowanej zarządzaniem, NAPRAWDĘ potrzebujesz opcjonalnego parametru out w C #, to pozwoli ci to zrobić.

wizard07KSU
źródło
6

ICYMI: Zawarte w nowych funkcjach wymienionych tutaj C # 7.0 , „odrzucanie” jest teraz dozwolone jako parametry wyjściowe w postaci _, co pozwala zignorować parametry, na których ci nie zależy:

p.GetCoordinates(out var x, out _); // I only care about x

PS, jeśli mylisz się także z częścią „out var x”, ​​przeczytaj nową funkcję o „Zmiennych wyjściowych” również w linku.

jbdeguzman
źródło
czy to _ lub * "p.GetCoordinates (out int x, out *); // Dbam tylko o x"
Onur Topal
wygląda na to, że szukałem starszej wersji dokumentu.
Onur Topal
2

Nie, ale możesz użyć delegata (np. Action) Jako alternatywy.

Zainspirowany częściowo odpowiedzią Robin R, gdy stanąłem przed sytuacją, w której myślałem, że chcę opcjonalnego parametru wyjściowego, zamiast tego użyłem Actiondelegata. Pożyczałem jego przykładowy kod do modyfikacji w celu użycia Action<int>w celu pokazania różnic i podobieństw:

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

Ma to tę zaletę, że opcjonalna zmienna pojawia się w źródle jako normalna int (kompilator otacza ją klasą zamknięcia, a nie zawija ją jawnie w klasie zdefiniowanej przez użytkownika).

Zmienna wymaga jawnej inicjalizacji, ponieważ kompilator nie może założyć, że Actionzostanie wywołany przed zakończeniem wywołania funkcji.

Nie nadaje się do wszystkich przypadków użycia, ale działał dobrze w moim prawdziwym przypadku użycia (funkcja, która zapewnia dane do testu jednostkowego, a nowy test jednostkowy wymagał dostępu do niektórych stanów wewnętrznych nieobecnych w wartości zwracanej).

Steve
źródło
0

Użyj metody przeciążonej bez parametru out, aby wywołać metodę z parametrem out dla C # 6.0 i niższych. Nie jestem pewien, dlaczego C # 7.0 dla .NET Core jest nawet poprawną odpowiedzią dla tego wątku, kiedy zapytano go konkretnie, czy C # 4.0 może mieć opcjonalny parametr wyjściowy. Odpowiedź brzmi nie!

Mr_CSharp
źródło
-2

Co powiesz na to?

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

Nadal musisz przekazać wartość do parametru z C #, ale jest to opcjonalny parametr referencyjny.

Aaron L.
źródło
8
„Nadal musisz przekazać wartość do parametru z C #” ... To sprawia, że ​​nie jest to opcjonalne.
Arturo Torres Sánchez
C # ignoruje [Optional]adnotację. To nie pomaga.
ToolmakerSteve
-4
void foo(ref int? n)
{
    return null;
}
użytkownik6469927
źródło
1
Dodaj wyjaśnienie do swojego kodu, aby wszyscy mogli łatwo zrozumieć tę koncepcję
pająk techniczny
1
Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, dlaczego i / lub jak odpowiada na pytanie, znacznie poprawiłoby jego długoterminową wartość. Proszę edytować swoje odpowiedzi, aby dodać trochę wyjaśnień.
Toby Speight,
2
Spowoduje to błąd składniowy, ponieważ metoda ma nieważny typ zwracany. Ponadto nie odpowiada na pytanie.
Nathan Montez