Czy należy deklarować metody wykorzystujące przeciążenia lub opcjonalne parametry w C# 4.0?

Oglądałem rozmowę Andersa na temat C# 4.0 i podglądu C# 5.0 i dało mi to do myślenia o tym, kiedy opcjonalne parametry są dostępne w C# jaki będzie zalecany sposób deklarowania metod, które nie wymagają podania wszystkich parametrów?

Na przykład klasa FileStream ma około piętnastu różnych konstruktorów, które można podzielić na logiczne "rodziny", np. te poniżej z ciągu znaków, te z IntPtr i te z a SafeFileHandle.

FileStream(string,FileMode);
FileStream(string,FileMode,FileAccess);
FileStream(string,FileMode,FileAccess,FileShare);
FileStream(string,FileMode,FileAccess,FileShare,int);
FileStream(string,FileMode,FileAccess,FileShare,int,bool);

Wydaje mi się, że tego typu wzór można uprościć mając zamiast tego trzy konstruktory i używając opcjonalnych parametrów dla tych, które mogą być niewykonane, co uczyniłoby różne rodziny konstruktorów bardziej odrębnymi [Uwaga: wiem, że ta zmiana nie zostanie wprowadzona w BCL, mówię hipotetycznie dla tego typu sytuacji].

Co o tym myślisz? Od C # 4.0 czy bardziej sensowne będzie tworzenie blisko spokrewnionych grup konstruktorów i metod a pojedyncza metoda z opcjonalnymi parametrami, czy jest dobry powód, aby trzymać się tradycyjnego mechanizmu wielu przeciążeń?
Author: Greg Beech, 2008-10-31

13 answers

Rozważyłbym następujące:

  • Czy twój kod musi być używany z języków, które nie obsługują opcjonalnych parametrów? Jeśli tak, rozważ uwzględnienie przeciążeń.
  • Czy masz członków w swoim zespole, którzy gwałtownie sprzeciwiają się opcjonalnym parametrom? (Czasami łatwiej jest żyć z decyzją, której się nie lubi, niż dyskutować o sprawie.)
  • czy jesteś pewien, że domyślne ustawienia nie zmienią się między kompilacjami Twojego kodu, lub jeśli to możliwe, czy Twoi rozmówcy będą w porządku z to?

Nie sprawdzałem, jak będą działać wartości domyślne, ale zakładam, że wartości domyślne zostaną zapisane w kodzie wywołującym, podobnie jak odniesienia do pól const. Zazwyczaj jest to w porządku - zmiany wartości domyślnej i tak są dość znaczące - ale to są rzeczy, które należy wziąć pod uwagę.

 107
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-30 21:55:12

Gdy metoda overload normalnie wykonuje to samo z inną liczbą argumentów, wtedy zostaną użyte wartości domyślne.

Gdy metoda overloading wykonuje funkcję w różny sposób w zależności od jej parametrów, to przeciążenie będzie nadal używane.

Użyłem opcjonalnego w moich dniach VB6 i od tego czasu go przegapiłem, zmniejszy to wiele duplikacji komentarzy XML w C#.

 17
Author: cfeduke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-30 21:47:37

Używam Delphi, z opcjonalnymi parametrami, od zawsze. zamiast tego przełączyłem się na używanie przeciążeń.

Ponieważ gdy będziesz tworzyć więcej przeciążeń, będziesz zawsze konfigurować opcjonalny formularz parametru; i tak będziesz musiał je przekonwertować na nieobowiązkowy.

I podoba mi się pogląd, że ogólnie jest jedna super metoda, a reszta to prostsze owijki wokół tej.

 11
Author: Ian Boyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 16:55:56

Na pewno skorzystam z opcjonalnej funkcji parametrów 4.0. Pozbywa się śmieszności ...

public void M1( string foo, string bar )
{
   // do that thang
}

public void M1( string foo )
{
  M1( foo, "bar default" ); // I have always hated this line of code specifically
}

... i umieszcza wartości dokładnie tam, gdzie rozmówca może je zobaczyć ...

public void M1( string foo, string bar = "bar default" )
{
   // do that thang
}
Znacznie prostsze i mniej podatne na błędy. Widziałem to jako błąd w sprawie przeciążenia ...
public void M1( string foo )
{
   M2( foo, "bar default" );  // oops!  I meant M1!
}

Nie grałem jeszcze z kompilatorem 4.0, ale nie byłbym zszokowany, gdy dowiedziałbym się, że kompilator po prostu emituje przeciążenia dla Ciebie.

 7
Author: JP Alioto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 16:53:38

Opcjonalne parametry są zasadniczo fragmentem metadanych, który kieruje kompilator przetwarzający wywołanie metody do wstawiania odpowiednich domyślnych wartości w miejscu wywołania. W przeciwieństwie do tego, przeciążenia zapewniają sposób, za pomocą którego kompilator może wybrać jedną z wielu metod, z których niektóre mogą same dostarczać wartości domyślne. Zauważ, że jeśli spróbujemy wywołać metodę, która określa opcjonalne parametry z kodu napisanego w języku, który ich nie obsługuje, kompilator będzie wymagał, aby należy podać parametry "opcjonalne", ale ponieważ wywołanie metody bez podania opcjonalnego parametru jest równoważne z wywołaniem jej z parametrem równym wartości domyślnej, nie ma przeszkód, aby takie języki wywoływały takie metody.

Znaczącą konsekwencją powiązania opcjonalnych parametrów na stronie wywołania jest to, że będą one przypisywane wartości na podstawie wersji kodu docelowego, która jest dostępna dla kompilatora. Jeśli zespół Foo mA metodę Boo(int) z domyślną wartość 5, a assembly Bar zawiera wywołanie Foo.Boo(), kompilator przetworzy to jako Foo.Boo(5). Jeśli wartość domyślna zostanie zmieniona na 6 i assembly Foo zostanie przekompilowane, Bar będzie nadal wywoływać Foo.Boo(5), chyba że lub dopóki nie zostanie przekompilowane z nową wersją Foo. Należy więc unikać używania opcjonalnych parametrów dla rzeczy, które mogą się zmienić.

 6
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-21 01:41:51

Czekam na opcjonalne parametry, ponieważ zachowuje to, co domyślne są bliżej metody. Tak więc zamiast dziesiątek linii przeciążeń, które wywołują metodę "rozszerzoną", po prostu zdefiniujesz metodę raz i możesz zobaczyć, jakie opcjonalne parametry są domyślne w sygnaturze metody. Wolę popatrzeć:

public Rectangle (Point start = Point.Zero, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

Zamiast tego:

public Rectangle (Point start, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

public Rectangle (int width, int height) :
    this (Point.Zero, width, height)
{
}

Oczywiście ten przykład jest naprawdę prosty, ale przypadek w OP z 5 przeciążeń, rzeczy mogą się zatłoczyć prawdziwe szybko.

 3
Author: Mark A. Nicolosi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-30 22:00:03

Można argumentować, czy argumenty opcjonalne lub przeciążenia powinny być używane, czy nie, ale co najważniejsze, każdy z nich ma swój własny obszar, w którym są niezastąpione.

Opcjonalne argumenty, gdy są używane w połączeniu z nazwanymi argumentami, są niezwykle przydatne w połączeniu z niektórymi długimi argumentami-listami-z-wszystkimi-opcjami wywołań COM.

Przeciążenia są niezwykle przydatne, gdy metoda jest w stanie operować na wielu różnych typach argumentów( tylko jednym z przykładów) i nie wewnętrznie, na przykład; po prostu przesyłasz go dowolnym typem danych, który ma sens (który jest akceptowany przez jakieś istniejące przeciążenie). Nie można tego pokonać opcjonalnymi argumentami.

 3
Author: mr.b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-21 10:16:13

Jednym z moich ulubionych aspektów opcjonalnych parametrów jest to, że widzisz, co dzieje się z Twoimi parametrami, jeśli ich nie podasz, nawet bez przechodzenia do definicji metody. Visual Studio wyświetli po prostu domyślną wartość dla parametru podczas wpisywania nazwy metody. W przypadku metody przeciążenia utkniesz albo z czytaniem dokumentacji (jeśli jest dostępna), albo z bezpośrednim przejściem do definicji metody (jeśli jest dostępna) i do metody, którą przeciążenie okłady.

W szczególności: nakład pracy na dokumentację może gwałtownie wzrosnąć wraz z ilością przeciążeń i prawdopodobnie skończysz kopiując już istniejące komentarze z istniejących przeciążeń. Jest to dość irytujące, ponieważ nie wytwarza żadnej wartości i łamie zasadę suchości ). Z drugiej strony, z opcjonalnym parametrem jest dokładnie jedno miejsce gdzie wszystkie parametry są udokumentowane i widzisz ich znaczenie, a także ich domyślne wartości podczas gdy pisanie.

Last but not least, if you are the consumer of a API you may not even have the option of inspection details (if you don ' t have the source code) and therefore have no chance to see to which Super method the overloaded ones are wrapping. W ten sposób utknąłeś w czytaniu dokumentu i masz nadzieję, że wszystkie wartości domyślne są tam wymienione, ale nie zawsze tak jest.

Oczywiście nie jest to odpowiedź, która zajmuje się wszystkimi aspektami, ale myślę, że dodaje jeden które dotychczas nie zostały uwzględnione.

 3
Author: HimBromBeere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-10 04:23:26

Jednym z zastrzeżeń opcjonalnych parametrów jest wersjonowanie, gdzie refaktor ma niezamierzone konsekwencje. Przykład:

Kod początkowy

public string HandleError(string message, bool silent=true, bool isCritical=true)
{
  ...
}

Załóżmy, że jest to jeden z wielu wywoływaczy powyższej metody:

HandleError("Disk is full", false);

Tutaj zdarzenie nie jest ciche i jest traktowane jako krytyczne.

Załóżmy, że po refaktorze okaże się, że wszystkie błędy i tak monitują użytkownika, więc nie potrzebujemy już flagi silent. Więc go usuniemy.

Po refaktor

The pierwsze wywołanie wciąż kompiluje się i powiedzmy, że prześlizguje się przez refaktor bez zmian:

public string HandleError(string message, /*bool silent=true,*/ bool isCritical=true)
{
  ...
}

...

// Some other distant code file:
HandleError("Disk is full", false);

Teraz false będzie miało niezamierzony efekt, zdarzenie nie będzie już traktowane jako krytyczne.

Może to spowodować subtelną wadę, ponieważ nie będzie żadnych błędów kompilacji ani uruchamiania (w przeciwieństwie do innych zastrzeżeń opcji, takich jak this lub this).

Zauważ, że istnieje wiele form tego samego problemu. Jedna inna forma jest nakreślona tutaj .

Zauważ również, że ścisłe użycie nazwanych parametrów podczas wywoływania metody pozwoli uniknąć problemu, na przykład tak: HandleError("Disk is full", silent:false). Jednak założenie, że zrobią to wszyscy inni programiści (lub użytkownicy publicznego API) może nie być praktyczne.

Z tych powodów unikałbym używania opcjonalnych parametrów w publicznym API (lub nawet publicznej metodzie, jeśli mogłaby być szeroko stosowana), chyba że istnieją inne istotne względy.

 1
Author: Zach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-07 23:00:35

Zarówno opcjonalny parametr, przeciążenie metody ma swoją przewagę lub disadvantage.it zależy od preferencji do wyboru między nimi.

Parametr Opcjonalny: dostępne tylko w. Net 4.0. opcjonalny parametr zmniejsz rozmiar kodu. Nie można zdefiniować parametru out i ref

Przeciążone metody: Możesz zdefiniować parametry Out i ref. Rozmiar kodu wzrośnie, ale przeciążone metody są łatwe do zrozumienia.

 0
Author: sushil pandey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-31 07:27:37

W wielu przypadkach opcjonalne parametry są używane do przełączania wykonania. Na przykład:

decimal GetPrice(string productName, decimal discountPercentage = 0)
{

    decimal basePrice = CalculateBasePrice(productName);

    if (discountPercentage > 0)
        return basePrice * (1 - discountPercentage / 100);
    else
        return basePrice;
}

Parametr Discount służy do podania instrukcji if-then-else. Istnieje polimorfizm, który nie został rozpoznany, a następnie został zaimplementowany jako twierdzenie if-then-else. W takich przypadkach znacznie lepiej jest podzielić dwa przepływy kontrolne na dwie niezależne metody:

decimal GetPrice(string productName)
{
    decimal basePrice = CalculateBasePrice(productName);
    return basePrice;
}

decimal GetPrice(string productName, decimal discountPercentage)
{

    if (discountPercentage <= 0)
        throw new ArgumentException();

    decimal basePrice = GetPrice(productName);

    decimal discountedPrice = basePrice * (1 - discountPercentage / 100);

    return discountedPrice;

}

W ten sposób zabezpieczyliśmy nawet klasę przed odebraniem połączenia z zerową zniżką. Ta rozmowa oznaczałaby że rozmówca myśli, że jest zniżka, ale w rzeczywistości nie ma zniżki w ogóle. Takie nieporozumienie może łatwo spowodować błąd.

W takich przypadkach wolę nie mieć opcjonalnych parametrów, ale wymusić na wywołującym jawny wybór scenariusza wykonania, który pasuje do jego obecnej sytuacji.

Sytuacja jest bardzo podobna do posiadania parametrów, które mogą być null. Jest to równie zły pomysł, gdy implementacja sprowadza się do wyrażeń takich jak if (x == null).

Możesz znaleźć szczegółowa analiza tych linków: unikanie parametrów opcjonalnych i unikanie parametrów Null

 0
Author: Zoran Horvat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-10 08:58:34

Póki są (podobno?) dwa koncepcyjnie równoważne sposoby modelowania API od podstaw, niestety mają one pewną subtelną różnicę, gdy trzeba wziąć pod uwagę kompatybilność wsteczną dla starych klientów w środowisku naturalnym. Mój kolega (dzięki!) zwrócił mi uwagę na ten wspaniały post: problemy z Wersjonowaniem z opcjonalnymi argumentami . Jakiś cytat z niego:

Powód, dla którego opcjonalne parametry zostały wprowadzone do C # 4 w pierwsze miejsce zajął aby wesprzeć COM interop. To wszystko. A teraz jesteśmy poznanie pełnych konsekwencji tego faktu. Jeśli masz metoda z opcjonalnymi parametrami, nigdy nie można dodać przeciążenia z dodatkowe opcjonalne parametry ze strachu przed wywołaniem kompilacji przełomowe zmiany. I nigdy nie można usunąć istniejącego przeciążenia, ponieważ to zawsze była zmiana przerwania pracy. You pretty much need traktować to jak interfejs. Jedynym rozwiązaniem w tym przypadku jest napisz nową metodę z nowe imię. Więc należy o tym pamiętać, jeśli planujesz użyj opcjonalnych argumentów w interfejsach API.

 0
Author: RayLuo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 19:58:34

Aby dodać bezmyślne, kiedy użyć przeciążenia zamiast opcji:

Gdy masz kilka parametrów, które mają sens tylko razem, nie wprowadzaj na nich opcji.

Lub bardziej ogólnie, ilekroć podpisy metod umożliwiają użycie wzorców, które nie mają sensu, ogranicz liczbę permutacji możliwych wywołań. Np. poprzez użycie przeciążeń zamiast opcji (reguła ta obowiązuje również wtedy, gdy masz kilka parametrów tego samego typu danych, przy okazji; tutaj mogą pomóc urządzenia takie jak metody fabryczne lub niestandardowe typy danych).

Przykład:

enum Match {
    Regex,
    Wildcard,
    ContainsString,
}

// Don't: This way, Enumerate() can be called in a way
//         which does not make sense:
IEnumerable<string> Enumerate(string searchPattern = null,
                              Match match = Match.Regex,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);

// Better: Provide only overloads which cannot be mis-used:
IEnumerable<string> Enumerate(SearchOption searchOption = SearchOption.TopDirectoryOnly);
IEnumerable<string> Enumerate(string searchPattern, Match match,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);
 0
Author: Sebastian Mach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-08 13:52:11