Oba generują błąd, który mówi, że muszą być stałą czasu kompilacji:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Przede wszystkim, czy ktoś może wyjaśnić, dlaczego tych wartości nie można określić w czasie kompilacji? Czy istnieje sposób na określenie wartości domyślnej dla opcjonalnego obiektu TimeSpan?
c#
c#-4.0
default-value
timespan
optional-parameters
Mike Pateras
źródło
źródło
new TimeSpan(2000)
nie oznacza to 2000 milisekund, ale 2000 „taktów”, co oznacza 0,2 milisekundy lub jedną 10.000 sekund.Odpowiedzi:
Możesz bardzo łatwo obejść ten problem, zmieniając swój podpis.
Powinienem to wyjaśnić - powodem, dla którego te wyrażenia w twoim przykładzie nie są stałe czasu kompilacji, jest to, że w czasie kompilacji kompilator nie może po prostu wykonać TimeSpan.FromSeconds (2.0) i umieścić bajtów wyniku w skompilowanym kodzie.
Jako przykład rozważ, czy zamiast tego próbowałeś użyć DateTime.Now. Wartość DateTime.Now zmienia się za każdym razem, gdy jest wykonywana. Albo załóżmy, że TimeSpan.FromSeconds wziął pod uwagę grawitację. To absurdalny przykład, ale reguły stałych czasu kompilacji nie tworzą specjalnych przypadków tylko dlatego, że wiemy, że TimeSpan.FromSeconds jest deterministyczny.
źródło
<param>
, ponieważ nie jest ona widoczna w podpisie.span = span ?? TimeSpan.FromSeconds(2.0);
z typem dopuszczającym wartość null w treści metody. Lubvar realSpan = span ?? TimeSpan.FromSeconds(2.0);
aby uzyskać zmienną lokalną, która nie dopuszcza wartości null.Moje dziedzictwo VB6 niepokoi mnie pomysł traktowania „wartości zerowej” i „wartości brakującej” jako równoważnych. W większości przypadków prawdopodobnie jest w porządku, ale możesz mieć niezamierzony efekt uboczny lub możesz połknąć wyjątkowy warunek (na przykład, jeśli źródłem
span
jest właściwość lub zmienna, która nie powinna mieć wartości null, ale jest).Dlatego przeładowałbym metodę:
źródło
To działa dobrze:
void Foo(TimeSpan span = default(TimeSpan))
źródło
TimeSpan
wartości, takie jak podane przeznew TimeSpan(2000)
.Zestaw wartości, które mogą być używane jako wartość domyślna, jest taki sam, jak może być używany dla argumentu atrybutu. Powodem jest to, że wartości domyślne są kodowane w metadanych wewnątrz
DefaultParameterValueAttribute
.Dlaczego nie można tego określić w czasie kompilacji. Zestaw wartości i wyrażeń przekraczających takie wartości dozwolone w czasie kompilacji jest wymieniony w oficjalnej specyfikacji języka C # :
Typ
TimeSpan
nie pasuje do żadnej z tych list i dlatego nie może być używany jako stała.źródło
TimeSpan
pasuje do ostatniego na tej liściedefault(TimeSpan)
jest ważny.podana
default(TimeSpan)
nie jest prawidłową wartością funkcji.Lub
podana
new TimeSpan()
nie jest prawidłową wartością.Lub
Powinno to być lepsze, biorąc pod uwagę, że szanse, że
null
wartość będzie prawidłową wartością dla funkcji, są rzadkie.źródło
TimeSpan
jest specjalnym przypadkiem dlaDefaultValueAttribute
i jest określany przy użyciu dowolnego łańcucha, który można przeanalizować za pomocąTimeSpan.Parse
metody.źródło
Moja sugestia:
BTW
TimeSpan.FromSeconds(2.0)
nie równa sięnew TimeSpan(2000)
- konstruktor bierze tiki.źródło
Inne odpowiedzi dały świetne wyjaśnienia, dlaczego opcjonalny parametr nie może być wyrażeniem dynamicznym. Ale żeby przypomnieć, parametry domyślne zachowują się jak stałe czasu kompilacji. Oznacza to, że kompilator musi być w stanie je ocenić i udzielić odpowiedzi. Są ludzie, którzy chcą, aby C # dodawał obsługę kompilatora oceniającego dynamiczne wyrażenia podczas napotykania stałych deklaracji - ten rodzaj funkcji byłby powiązany z oznaczaniem metod jako „czystych”, ale to nie jest obecnie rzeczywistością i może nigdy nie być.
Jedną z alternatyw dla użycia domyślnego parametru C # dla takiej metody byłoby użycie wzorca, którego przykładem jest
XmlReaderSettings
. W tym wzorcu zdefiniuj klasę z konstruktorem bez parametrów i publicznie zapisywalnymi właściwościami. Następnie zamień wszystkie opcje na wartości domyślne w swojej metodzie na obiekt tego typu. Nawet ustaw ten obiekt jako opcjonalny, określającnull
dla niego wartość domyślną . Na przykład:Aby wywołać, użyj tej dziwnej składni do tworzenia wystąpienia i przypisywania właściwości w jednym wyrażeniu:
Wady
To naprawdę ciężkie podejście do rozwiązania tego problemu. Jeśli piszesz szybki i brudny interfejsu wewnętrznego i dokonywania
TimeSpan
zerowalne i leczenia NULL jak żądanej wartości domyślnej będzie działać dobrze, że zamiast robić.Ponadto, jeśli masz dużą liczbę parametrów lub wywołujesz metodę w ścisłej pętli, będzie to miało narzut związany z tworzeniem instancji klas. Oczywiście wywołanie takiej metody w ciasnej pętli może być naturalne, a nawet bardzo łatwe do ponownego wykorzystania instancji
FooSettings
obiektu.Korzyści
Jak wspomniałem w komentarzu w przykładzie, myślę, że ten wzorzec jest świetny dla publicznych interfejsów API. Dodanie nowych właściwości do klasy jest niezepsutą zmianą ABI, więc możesz dodawać nowe parametry opcjonalne bez zmiany podpisu metody przy użyciu tego wzorca - dając ostatnio skompilowanemu kodowi więcej opcji, jednocześnie kontynuując obsługę starego skompilowanego kodu bez dodatkowej pracy .
Ponadto, ponieważ wbudowane w C # domyślne parametry metod są traktowane jako stałe kompilacji i umieszczane na stronie wywołania, parametry domyślne będą używane przez kod dopiero po ponownej kompilacji. Tworząc wystąpienie obiektu ustawień, obiekt wywołujący dynamicznie ładuje wartości domyślne podczas wywoływania metody. Oznacza to, że możesz zaktualizować ustawienia domyślne, po prostu zmieniając klasę ustawień. Dlatego ten wzorzec umożliwia zmianę wartości domyślnych bez konieczności ponownej kompilacji wywołań w celu wyświetlenia nowych wartości, jeśli jest to wymagane.
źródło