Jak wyraźnie odrzucić argument zewnętrzny?


103

Dzwonię:

myResult = MakeMyCall(inputParams, out messages);

ale tak naprawdę nie obchodzą mnie wiadomości. Gdyby to był parametr wejściowy, na którym mi nie zależało, po prostu podałbym wartość null. Gdyby to był powrót, na którym mi nie zależało, zostawiłbym to.

Czy istnieje sposób na zrobienie czegoś podobnego bez wyjścia, czy też muszę zadeklarować zmienną, którą następnie zignoruję?




Dzięki! Szkoda, że ​​nie ma go w najnowszej wersji.
Andrew Ducker,

Odpowiedzi:


103

Począwszy od języka C # 7.0, można uniknąć wcześniejszego deklarowania parametrów, a także ich ignorowania.

public void PrintCoordinates(Point p)
{
    p.GetCoordinates(out int x, out int y);
    WriteLine($"({x}, {y})");
}

public void PrintXCoordinate(Point p)
{
    p.GetCoordinates(out int x, out _); // I only care about x
    WriteLine($"{x}");
}

Źródło: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/


1
Niestety, nie jest to uwzględnione w C # 7 (stan na kwiecień 2017).
tia

2
@tia. Zaktualizowałem odpowiedź. Najwyraźniej znak wieloznaczny został zmieniony z *na _. Przepraszam, że trwało to tak długo.
Nolonar

14
Powinni byli trzymać się swojego pomysłu na użycie out voidskładni, a podkreślenie wydaje się dziwnym wyborem.
David Anderson,

1
@ DavidAnderson-DCOM Chociaż nie jestem fanem podkreślenia, myślę, że potrzebują nazwy typu, aby rozwiązać problem z przeciążeniem.
Jonathan Allen

Wygląda na to, że nadal tworzy parametr, ponieważ mogę dodać wiersz _ = {a value};po wywołaniu funkcji bez błędów kompilacji.
Bip901

37

Niestety musisz coś przekazać, ponieważ metoda jest wymagana, aby to ustawić. Więc nie możesz wysłać, nullponieważ metoda, która byłaby wymagana do jej ustawienia, wybuchłaby.

Jednym ze sposobów na ukrycie brzydoty byłoby zawinięcie metody w inną metodę, która wykonuje outparametr za Ciebie:

String Other_MakeMyCall(String inputParams)
{
    String messages;

    return MakeMyCall(inputParams, out messages);
}

Wtedy możesz zadzwonić Other_MakeMyCallbez konieczności manipulowaniaout parametrach, których nie potrzebujesz.


37

Musisz zadeklarować zmienną, którą następnie zignorujesz. Dzieje się tak najczęściej w przypadku wzorca TryParse (lub TryWhthing), gdy jest on używany do testowania poprawności danych wejściowych użytkownika (np. Czy można je przeanalizować jako liczbę?) Bez zwracania uwagi na rzeczywistą przeanalizowaną wartość.

W pytaniu użyłeś słowa „dispose”, co, jak podejrzewam, było po prostu niefortunne - ale jeśli parametr out jest typu, który implementuje IDisposable, z pewnością powinieneś wywołać Dispose, chyba że dokumentacja metody wyraźnie stwierdza, że ​​otrzymanie wartości nie nadaje własność. Nie pamiętam outjednak, żeby kiedykolwiek widziałem metodę z jednorazowym parametrem, więc mam nadzieję, że był to po prostu niefortunny dobór słów.


Bardziej pragmatyczny anty-wzór
Jodrell

11

Jeśli oryginalna funkcja jest zadeklarowana w następujący sposób:

class C
{
    public Result MakeMyCall(Object arg, out List<String> messages);
}

Możesz zadeklarować taką metodę rozszerzenia:

static class CExtension
{
    public static Result MakeMyCall(this C obj, Object arg)
    {
        List<String> unused;
        return obj.MakeMyCall(arg, out unused);
    }
}

Metoda rozszerzenia będzie zachowywać się jak przeciążenie, które powoduje, że parametr out jest opcjonalny.


4

Kompilator Visual Basic robi to, tworząc zmienną fikcyjną. C # mógłby to zrobić, jeśli przekonasz Microsoft, że to dobry pomysł.


0

Jeśli chodzi o klasę messagesnarzędzi IDisposable, nie powinieneś jej ignorować. Rozważ coś takiego jak następujące podejście (może nie być poprawne składniowo, ponieważ od jakiegoś czasu nie pisałem C #):

using (FooClass messages) {
    myResult = MakeMyCall(inputParams, messages);
}

Po wyjściu z usingbloku messageszostanie usunięty automatycznie.


1
Ciekawy. Ale czy nie musisz inicjalizować zmiennej w instrukcji using?
OregonGhost,

1
@OregonGhost: Tak, masz. A jeśli zmienisz wartość zmiennej w instrukcji using, to nadal oryginalna wartość zostanie usunięta.
Jon Skeet

@JonSkeet Nie można uchwycić, że nadal jest oryginalna wartość, która została usunięta.
Orkhan Alikhanov

@OrkhanAlikhanov: Kompilator w zasadzie pobiera kopię zmiennej na początku usinginstrukcji. Zatem zmiana wartości tej zmiennej w bloku nie zmienia tego, który obiekt zostanie usunięty.
Jon Skeet,

Nawiasem mówiąc, powinno być out messages.
Orkhan Alikhanov

0

Musisz przekazać zmienną dla parametru out. Nie musisz inicjalizować zmiennej przed jej przekazaniem:

MyMessagesType messages;
myResult = MakeMyCall(inputParams, out messages); 

Zwykle możesz po prostu zignorować `` wiadomości '' po wywołaniu - chyba że `` wiadomości '' wymagają usunięcia z jakiegoś powodu, takiego jak użycie ograniczonych zasobów systemowych, w takim przypadku powinieneś wywołać Dispose ():

messages.Dispose();

Jeśli może zużywać znaczną ilość pamięci i przez jakiś czas pozostanie w zasięgu, prawdopodobnie powinien być ustawiony na wartość null, jeśli jest to typ referencyjny, lub na nową domyślną instancję, jeśli jest to typ wartości, tak aby śmieci kolekcjoner może odzyskać pamięć:

messages = null; // Allow GC to reclaim memory for reference type.

messages = new MyMessageType(); // Allow GC to reclaim memory for value type.

0

W tym przypadku stworzyłem ogólną metodę rozszerzenia dla ConcurrentDictionary, która nie ma metody Delete ani Remove.

//Remove item from list and ignore reference to removed item
public static void TryRemoveIgnore<K,T>(this ConcurrentDictionary<K,T> dictionary, K key)
{
    T CompletelyIgnored;
    dictionary.TryRemove(key, out CompletelyIgnored);
}

W przypadku wywołania z wystąpienia ConcurrentDictionary:

ClientList.TryRemoveIgnore(client.ClientId);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.