W języku C # out
słowa kluczowego można używać na dwa różne sposoby.
Jako modyfikator parametru, w którym argument jest przekazywany przez odwołanie
class OutExample { static void Method(out int i) { i = 44; } static void Main() { int value; Method(out value); // value is now 44 } }
Jako modyfikator parametru typu do określania kowariancji .
// Covariant interface. interface ICovariant<out R> { } // Extending covariant interface. interface IExtCovariant<out R> : ICovariant<R> { } // Implementing covariant interface. class Sample<R> : ICovariant<R> { } class Program { static void Test() { ICovariant<Object> iobj = new Sample<Object>(); ICovariant<String> istr = new Sample<String>(); // You can assign istr to iobj because // the ICovariant interface is covariant. iobj = istr; } }
Moje pytanie brzmi: dlaczego?
Dla początkującego połączenie między nimi nie wydaje się intuicyjne . Wydaje się, że użycie z rodzajami nie ma nic wspólnego z przekazywaniem przez referencję.
Najpierw nauczyłem się, co out
jest związane z przekazywaniem argumentów przez odniesienie, i to utrudniło mi zrozumienie użycia definicji kowariancji z rodzajami.
Czy istnieje związek między tymi zastosowaniami, których mi brakuje?
c#
terminology
language-design
keywords
Rowan Freeman
źródło
źródło
System.Func<in T, out TResult>
delegacie .Odpowiedzi:
Istnieje połączenie, jednak jest nieco luźne. W języku C # słowa kluczowe „in” i „out”, jak ich nazwa sugeruje, oznaczają wejście i wyjście. Jest to bardzo jasne w przypadku parametrów wyjściowych, ale mniej czyste, co to ma wspólnego z parametrami szablonu.
Rzućmy okiem na zasadę substytucji Liskowa :
Zobacz, w jaki sposób kontrawariancja jest powiązana z danymi wejściowymi, a kowariancja jest powiązana z danymi wyjściowymi? W języku C # jeśli oflagujesz zmienną szablonu,
out
aby była kowariantna, ale pamiętaj, że możesz to zrobić tylko wtedy, gdy wspomniany parametr type pojawia się tylko jako wynik (typ zwracanej funkcji). Zatem następujące informacje są nieprawidłowe:Podobnie, jeśli oflagujesz parametr typu
in
, co oznacza, że możesz go używać tylko jako danych wejściowych (parametr funkcji). Zatem następujące informacje są nieprawidłowe:Podsumowując, połączenie ze
out
słowem kluczowym polega na tym, że w przypadku parametrów funkcji oznacza to, że jest to parametr wyjściowy , a dla parametrów typu oznacza, że typ jest używany tylko w kontekście wyjściowym .System.Func
jest również dobrym przykładem tego, co rwong wspomniał w swoim komentarzu. WeSystem.Func
wszystkich parametrach wejściowych są oznaczonein
, a parametr wyjściowy jest oznaczoneout
. Powodem jest dokładnie to, co opisałem.źródło
@ Gábor wyjaśnił już związek (sprzeczność dla wszystkiego, co wchodzi „w”, kowariancja dla wszystkiego, co wychodzi „)”, ale po co w ogóle ponownie używać słów kluczowych?
Cóż, słowa kluczowe są bardzo drogie. Nie możesz ich używać jako identyfikatorów w swoich programach. Ale w języku angielskim jest tylko tyle słów. Czasami więc napotykasz konflikty i musisz niezręcznie zmieniać nazwy swoich zmiennych, metod, pól, właściwości, klas, interfejsów lub struktur, aby uniknąć kolizji ze słowem kluczowym. Na przykład, jeśli modelujesz szkołę, jak nazywasz klasę? Nie można tego nazwać klasą, ponieważ
class
jest słowem kluczowym!Dodawanie słowa kluczowego do języka jest jeszcze bardziej kosztowne. Zasadniczo sprawia, że cały kod, który używa tego słowa kluczowego jako identyfikatora, jest nielegalny, przerywając kompatybilność wsteczną w każdym miejscu.
in
Iout
słowa kluczowe już istnieje, więc może po prostu być ponownie wykorzystane.Oni mogli dodałem słowo kontekstowych, które są tylko słowa kluczowe w kontekście listy parametrów typu, ale jakie słowa kluczowe by wybrali?
covariant
icontravariant
?+
i-
(jak na przykład Scala)?super
iextends
jak Java? Czy pamiętasz z góry głowy, które parametry są kowariantne i sprzeczne?W obecnym rozwiązaniu istnieje ładny mnemonik: parametry typu wyjściowego uzyskują
out
słowo kluczowe, parametry typu wejściowego uzyskująin
słowo kluczowe. Zwróć uwagę na dobrą symetrię z parametrami metody: parametry wyjściowe otrzymująout
słowo kluczowe, parametry wejściowe otrzymująin
słowo kluczowe (właściwie nie ma w ogóle słowa kluczowego, ponieważ wejście jest domyślne, ale masz pomysł).[Uwaga: jeśli spojrzysz na historię edycji, zobaczysz, że tak naprawdę pierwotnie zamieniłem je w zdaniu wprowadzającym. W tym czasie dostałem nawet głos! To po prostu pokazuje, jak ważny jest ten mnemonik.]
źródło
interface Accepter<in T> { void Accept(T it);};
, toAccepter<Foo<T>>
zaakceptujeT
jako parametr wejściowy, jeśliFoo<T>
zaakceptuje go jako parametr wyjściowy i odwrotnie. Zatem przeciwwariancja . Natomiast będzie miał cokolwiek rodzaju wariancji ma - stąd współpracy -variance.interface ISupplier<out T> { T get();};
Supplier<Foo<T>>
Foo