Jak mogę domyślny parametr do Guid.Empty w C #?

178

Chcę powiedzieć:

public void Problem(Guid optional = Guid.Empty)
{
}

Ale kompilator narzeka, że ​​Guid.Empty nie jest stałą czasową kompilacji.

Ponieważ nie chcę zmieniać interfejsu API, nie mogę użyć:

 Nullable<Guid>
Ian Ringrose
źródło
Co jest złego w przejściu na Nullable<Guid> optional = null(lub bardziej zwięźle Guid? optional = null)? Wszelkie Guidy aktualnie przekazane do niego będą wymuszane bez konieczności wprowadzania jakichkolwiek zmian w kodzie.
NH.

Odpowiedzi:

235

Rozwiązanie

Możesz użyć new Guid()zamiast tego

public void Problem(Guid optional = new Guid())
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Możesz także użyć default(Guid)

default(Guid)również będzie działać dokładnie tak, jak new Guid().

Ponieważ Guid jest typem wartości, a nie typem referencyjnym, więc default(Guid)nie jest równy nullna przykład, zamiast tego jest równy wywołaniu domyślnego konstruktora.

Co oznacza, że:

public void Problem(Guid optional = default(Guid))
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Jest dokładnie taki sam jak oryginalny przykład.

Wyjaśnienie

Dlaczego nie Guid.Emptyzadziałało?

Powód, dla którego otrzymujesz błąd, jest Emptynastępujący:

public static readonly Guid Empty;

Jest to więc zmienna, a nie stała (zdefiniowana jako static readonlynie jako const). Kompilator może mieć tylko wartości znane kompilatorowi jako wartości domyślne parametrów metody (nie znane tylko w czasie wykonywania).

Główną przyczyną jest to, że nie możesz mieć constżadnego struct, w przeciwieństwie enumdo na przykład. Jeśli spróbujesz, nie skompiluje się.

Powód jest taki, że structnie jest to typ prymitywny.
Aby zapoznać się z listą wszystkich typów pierwotnych w .NET, zobacz http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(zauważ, że enumzwykle dziedziczy int, co jest prymitywem)

Ale new Guid()nie jest też stałą!

Nie mówię, że to wymaga stałej. Potrzebuje czegoś, co można zdecydować w czasie kompilacji. Emptyjest polem, więc jego wartość nie jest znana w czasie kompilacji (tylko na samym początku działania).

Domyślna wartość parametru musi być znana w czasie kompilacji, która może być constwartością lub czymś zdefiniowanym za pomocą funkcji C #, która sprawia, że ​​wartość jest znana w czasie kompilacji, na przykład default(Guid)lub new Guid()(co jest ustalane w czasie kompilacji dla structs, ponieważ nie można modyfikować structkonstruktora w kod).

Chociaż możesz podać defaultlub newłatwo, nie możesz podać const(ponieważ nie jest to typ pierwotny ani, enumjak wyjaśniono powyżej). Tak więc, ponownie, nie mówiąc, że opcjonalny parametr sam w sobie wymaga stałej, ale wartość znana kompilatorowi.

Meligy
źródło
5
Cóż, nie potrzebuje normalnego wyrażenia stałego - new Guid()na przykład nie jest wyrażeniem stałym. Specyfikacja języka C # dość jasno definiuje, co jest dozwolone, w tym między innymi stałe. (Żeby było jasne, w praktyce jest to stała czasu kompilacji, ale nie jest „stałym wyrażeniem” w terminach C # spec).
Jon Skeet,
3
Przeczytaj część Wyjaśnienie w mojej odpowiedzi. Dodano odpowiedź na tę część.
Meligy,
Niezłe wyjaśnienie, dzięki!
Daryl
Możesz używać tylko defaultdzisiaj :)
Joshit
151

Guid.Emptyjest równoważne z new Guid(), co jest równoważne z default(Guid). Możesz więc użyć:

public void Problem(Guid optional = default(Guid))

lub

public void Problem(Guid optional = new Guid())

Należy pamiętać, że new Foo()wartość ma zastosowanie tylko wtedy, gdy:

  • Naprawdę wywołujesz konstruktor bez parametrów
  • Foo jest typem wartości

Innymi słowy, gdy kompilator wie, że tak naprawdę jest to tylko domyślna wartość typu :)

(Co ciekawe, jestem na 99,9% pewien, że nie wywoła żadnego niestandardowego new Foo()konstruktora, który mógłbyś stworzyć. Nie możesz utworzyć takiego konstruktora w typie wartości w C #, ale możesz to zrobić w IL.)

Możesz użyć tej default(Foo)opcji dla dowolnego typu.

Jon Skeet
źródło
Dlaczego teraz komunikat o błędzie kompilatora nie mówi mi tego, osoba odpowiedzialna za zgodność może sprawdzić przypadek Guid.Empty i podać bardziej pomocny komunikat.
Ian Ringrose,
4
@Ian Ringrose: Szczerze mówiąc, nie sądzę, aby kompilator zawierał komunikaty specyficzne dla typu.
Jon Skeet,
2
Ustawienie parametru na wartość domyślną nowego obiektu powoduje utworzenie nowego obiektu za każdym razem, gdy metoda jest wywoływana w PHP; ale tworzy tylko jeden obiekt dla całego programu w Pythonie. Naprawdę uważam to za jedną z nielicznych wad projektowych Pythona . Jestem trochę zadowolony, że C # (i VB.Net) uniknęli tego problemu, po prostu blokując nowe obiekty w domyślnych parametrach ... chociaż są chwile, w których ta umiejętność jest naprawdę dobra w PHP.
BlueRaja - Danny Pflughoeft
1
Jaki może być powód, dla którego ta sama rzecz nie zadziała w akcji kontrolera ASP.NET MVC? Wszystkie inne parametry opcjonalne działają (int, string), ale nie działają w przypadku identyfikatora GUID, mówi „Błąd serwera w aplikacji„ / ”. Słownik parametrów zawiera pusty wpis dla parametru„ categoryId ”typu„ System.Guid nie dopuszczającego wartości null ”. '.. "Kod kompiluje się dobrze z obiema specyfikacjami (domyślna (Guid) i nowa Guid ()), ale generuje ten błąd.
klacz
@mare: Nie wiem, obawiam się. Inną opcją byłoby użycie Nullable<Guid>potencjalnie.
Jon Skeet
18

Nie możesz użyć:

default ( Guid ) ?

Nacięcie
źródło
1
Nie. Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
rekurencyjne
4
Przepraszam, nie miałem na myśli ?? jako operator, ale jako podkreślony znak zapytania - będę edytować!
Nick
9

Zaakceptowana odpowiedź nie działa w ASP.NET MVC i powoduje ten błąd w czasie wykonywania:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'optional' of non-nullable type 'System.Guid' for method 'System.Web.Mvc.ActionResult Problem(System.Guid)' ....

Zamiast tego możesz wykonać następujące czynności:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
źródło
Widzę powód głosowania w dół: pytanie określa, że ​​API nie można zmienić, aby używał Nullable <Guid> - w porządku
Majix
Jeśli nie chcesz jawnie przypisać parametrowi „opcjonalny” pustej wartości Guid, myślę, że jest to najbardziej naturalny sposób definiowania opcjonalnego parametru typu Guid.
Gonzalo Méndez
4

Kompilator jest całkiem poprawny; Guid.Emptynie jest stałą czasu kompilacji. Możesz spróbować przeciążać metodę w następujący sposób:

public void Problem()
{
    Problem(Guid.Empty);
}
CVn
źródło
Powiedziałem, że nie chcę zmieniać API, metoda, którą próbuję oswoić, przekroczyła 10 par!
Ian Ringrose,
@Ian Ringrose, chociaż zgadzam się z Guid x = default(Guid)rozwiązaniem, pamiętaj, że dodanie kolejnej funkcji przeciążenia nie komplikuje API bardziej niż dodanie opcjonalnego argumentu. I tak naprawdę to robi opcjonalny argument.
tenfour
@tenfour, musiałoby być wiele nowych przeciążeń funkcji, aby zrobić to samo, co 10 opcjonalnych parametrów!
Ian Ringrose,
Jeśli masz funkcję publiczną, która wymaga ponad dziesięciu parametrów, być może uczynienie pojedynczego argumentu opcjonalnym nie jest tak naprawdę rozwiązaniem ... Ponadto, patrząc wstecz na pierwotne pytanie, rzeczywiście mówisz, że nie chcesz „zmieniać API ”(i jak wskazuje tenfour, różnica między jawnym przeciążeniem a opcjonalnym argumentem jest w praktyce minimalna), ale w kwestii listy parametrów nie ma żadnej wzmianki o tym, że lista parametrów jest tego rodzaju potworem.
CVn
Właściwie jest to doskonała odpowiedź na problem.
PKD