Szukam odpowiednika języka Java w C # final
. Czy to istnieje?
Czy C # ma coś podobnego do następującego:
public Foo(final int bar);
W powyższym przykładzie bar
jest zmienną tylko do odczytu i nie można jej zmienić Foo()
. Czy jest jakiś sposób, aby to zrobić w C #?
Na przykład, może mam długą metodę, która będzie pracować z x
, y
i z
współrzędne jakiegoś przedmiotu (int). Chcę mieć absolutną pewność, że funkcja w żaden sposób nie zmienia tych wartości, uszkadzając w ten sposób dane. Dlatego chciałbym zadeklarować je tylko do odczytu.
public Foo(int x, int y, int z) {
// do stuff
x++; // oops. This corrupts the data. Can this be caught at compile time?
// do more stuff, assuming x is still the original value.
}
c#
constants
readonly
parameter-passing
Nick Heiner
źródło
źródło
Odpowiedzi:
Niestety nie możesz tego zrobić w C #.
Słowa
const
kluczowego można używać tylko w przypadku zmiennych i pól lokalnych.Słowa
readonly
kluczowego można używać tylko w polach.z http://www.25hoursaday.com/CsharpVsJava.html
EDYCJA (2019/08/13): Wrzucam to dla widoczności, ponieważ jest to akceptowane i najwyższe na liście. Jest to teraz możliwe dzięki
in
parametrom. Zobacz odpowiedź poniżej, aby uzyskać szczegółowe informacje.źródło
Jest to teraz możliwe w C # wersji 7.2:
Możesz użyć
in
słowa kluczowego w sygnaturze metody. Dokumentacja MSDN .Słowo
in
kluczowe należy dodać przed określeniem argumentu metody.Przykład, prawidłowa metoda w C # 7.2:
public long Add(in long x, in long y) { return x + y; }
Chociaż nie są dozwolone:
public long Add(in long x, in long y) { x = 10; // It is not allowed to modify an in-argument. return x + y; }
Poniższy błąd zostanie wyświetlony podczas próby zmodyfikowania
x
luby
po oznaczeniuin
:Oznaczanie argumentu
in
środkami:źródło
in
słowa kluczowego dla parametrów o tych typach zapobiega tylko przypisywaniu do nich. Nie zapobiega modyfikowaniu jego dostępnych pól lub właściwości! Czy mam rację?Oto krótka i słodka odpowiedź, która prawdopodobnie otrzyma wiele głosów negatywnych. Nie przeczytałem wszystkich postów i komentarzy, więc proszę mi wybaczyć, jeśli zostało to wcześniej zasugerowane.
Dlaczego nie wziąć parametrów i nie przekazać ich do obiektu, który ujawnia je jako niezmienne, a następnie użyć tego obiektu w swojej metodzie?
Zdaję sobie sprawę, że jest to prawdopodobnie bardzo oczywiste obejście, które zostało już rozważone, a OP próbuje tego uniknąć, zadając to pytanie, ale czułem, że powinno być tutaj ...
Powodzenia :-)
źródło
int* p; *p = 0;
. Spowoduje to skompilowanie i uruchomienie aż do błędu segmentacji.Odpowiedź: C # nie ma funkcji const, takiej jak C ++.
Zgadzam się z Bennettem Dillem.
Słowo kluczowe const jest bardzo przydatne. W tym przykładzie użyłeś int i ludzie nie rozumieją, o co ci chodzi. Ale dlaczego, jeśli parametr jest dużym i złożonym obiektem użytkownika, którego nie można zmienić w tej funkcji? To jest użycie słowa kluczowego const: parametr nie może się zmienić w tej metodzie, ponieważ [z jakiegokolwiek powodu] to nie ma znaczenia dla tej metody. Słowo kluczowe const jest bardzo potężne i bardzo mi go brakuje w C #.
źródło
Zacznę od
int
porcji.int
jest typem wartości, aw .Net oznacza to, że naprawdę masz do czynienia z kopią. To naprawdę dziwne ograniczenie projektowe, aby powiedzieć metodzie „Możesz mieć kopię tej wartości. To twoja kopia, nie moja; nigdy więcej jej nie zobaczę. Ale nie możesz zmienić kopii”. W wywołaniu metody jest niejawne, że kopiowanie tej wartości jest w porządku, w przeciwnym razie nie moglibyśmy bezpiecznie wywołać metody. Jeśli metoda wymaga oryginału, pozostaw to osobie wdrażającej, aby wykonała kopię w celu jej zapisania. Albo nadaj metodzie wartość, albo nie nadaj metodzie wartości. Nie idź między tymi prymitywnymi pragnieniami.Przejdźmy do typów referencyjnych. Teraz robi się trochę zagmatwane. Czy masz na myśli stałe odniesienie, w którym nie można zmienić samego odniesienia, czy też całkowicie zablokowany, niezmienny obiekt? W pierwszym przypadku odwołania w .Net są domyślnie przekazywane według wartości. Oznacza to, że otrzymujesz kopię referencji. Mamy więc zasadniczo taką samą sytuację, jak w przypadku typów wartości. Jeśli osoba wdrażająca będzie potrzebować oryginalnego odniesienia, może je zachować samodzielnie.
To po prostu pozostawia nam stały (zablokowany / niezmienny) obiekt. Może się to wydawać w porządku z perspektywy czasu wykonywania, ale jak kompilator ma to wymusić? Ponieważ wszystkie właściwości i metody mogą mieć skutki uboczne, zasadniczo byłbyś ograniczony do dostępu do pola tylko do odczytu. Taki obiekt raczej nie będzie interesujący.
źródło
Utwórz interfejs dla swojej klasy, który ma tylko metody dostępu do właściwości tylko do odczytu. Następnie ustaw parametr na tym interfejsie, a nie na samej klasie. Przykład:
public interface IExample { int ReadonlyValue { get; } } public class Example : IExample { public int Value { get; set; } public int ReadonlyValue { get { return this.Value; } } } public void Foo(IExample example) { // Now only has access to the get accessors for the properties }
W przypadku struktur utwórz ogólne opakowanie const.
public struct Const<T> { public T Value { get; private set; } public Const(T value) { this.Value = value; } } public Foo(Const<float> X, Const<float> Y, Const<float> Z) { // Can only read these values }
Warto jednak zauważyć, że to dziwne, że chcesz robić to, o co prosisz w odniesieniu do struktur, jako autor metody, powinieneś się spodziewać, że wiesz, co się dzieje w tej metodzie. Nie wpłynie to na wartości przekazywane w celu zmodyfikowania ich w ramach metody, więc Twoim jedynym zmartwieniem jest upewnienie się, że zachowujesz się w metodzie, którą piszesz. Przychodzi moment, w którym czujność i czysty kod są kluczem do egzekwowania const i innych tego typu reguł.
źródło
Jeśli często masz takie kłopoty, powinieneś rozważyć „węgierskie aplikacje”. Dobry, w przeciwieństwie do złego . Chociaż zwykle nie próbuje to wyrazić stałości parametru metody (jest to po prostu zbyt niezwykłe), z pewnością nic nie powstrzymuje cię przed dodaniem dodatkowego „c” przed nazwą identyfikatora.
Wszystkim, którzy chcą teraz wcisnąć przycisk `` przeciw '', przeczytajcie opinie tych luminarzy na ten temat:
źródło
Wiem, że to może być trochę za późno. Ale dla osób, które wciąż szukają innych sposobów, może istnieć inny sposób obejścia tego ograniczenia standardu C #. Moglibyśmy napisać klasę opakowującą ReadOnly <T>, gdzie T: struct. Z niejawną konwersją do typu podstawowego T. Ale tylko jawna konwersja na klasę wrapper <T>. Który wymusi błędy kompilatora, jeśli deweloper spróbuje niejawnie ustawić wartość typu ReadOnly <T>. Jak pokażę poniżej dwa możliwe zastosowania.
USAGE 1 wymagało zmiany definicji rozmówcy. To użycie będzie używane tylko do testowania poprawności kodu funkcji „TestCalled”. Na poziomie wydania / kompilacji nie powinieneś go używać. Ponieważ operacje matematyczne na dużą skalę mogą nadmiernie wpływać na konwersje i spowalniać kod. Nie użyłbym tego, ale zamieściłem go tylko w celach demonstracyjnych.
ZASTOSOWANIE 2, które sugerowałbym, ma debugowanie vs wydanie pokazane w funkcji TestCalled2. Również nie byłoby konwersji w funkcji TestCaller przy zastosowaniu tego podejścia, ale wymaga to trochę więcej kodowania definicji TestCaller2 przy użyciu warunkowania kompilatora. Możesz zauważyć błędy kompilatora w konfiguracji debugowania, podczas gdy w konfiguracji wydania cały kod w funkcji TestCalled2 zostanie pomyślnie skompilowany.
using System; using System.Collections.Generic; public class ReadOnly<VT> where VT : struct { private VT value; public ReadOnly(VT value) { this.value = value; } public static implicit operator VT(ReadOnly<VT> rvalue) { return rvalue.value; } public static explicit operator ReadOnly<VT>(VT rvalue) { return new ReadOnly<VT>(rvalue); } } public static class TestFunctionArguments { static void TestCall() { long a = 0; // CALL USAGE 1. // explicite cast must exist in call to this function // and clearly states it will be readonly inside TestCalled function. TestCalled(a); // invalid call, we must explicit cast to ReadOnly<T> TestCalled((ReadOnly<long>)a); // explicit cast to ReadOnly<T> // CALL USAGE 2. // Debug vs Release call has no difference - no compiler errors TestCalled2(a); } // ARG USAGE 1. static void TestCalled(ReadOnly<long> a) { // invalid operations, compiler errors a = 10L; a += 2L; a -= 2L; a *= 2L; a /= 2L; a++; a--; // valid operations long l; l = a + 2; l = a - 2; l = a * 2; l = a / 2; l = a ^ 2; l = a | 2; l = a & 2; l = a << 2; l = a >> 2; l = ~a; } // ARG USAGE 2. #if DEBUG static void TestCalled2(long a2_writable) { ReadOnly<long> a = new ReadOnly<long>(a2_writable); #else static void TestCalled2(long a) { #endif // invalid operations // compiler will have errors in debug configuration // compiler will compile in release a = 10L; a += 2L; a -= 2L; a *= 2L; a /= 2L; a++; a--; // valid operations // compiler will compile in both, debug and release configurations long l; l = a + 2; l = a - 2; l = a * 2; l = a / 2; l = a ^ 2; l = a | 2; l = a & 2; l = a << 2; l = a >> 2; l = ~a; } }
źródło
Jeśli struct zostanie przekazana do metody, chyba że zostanie przekazana przez ref, nie zostanie zmieniona przez metodę, do której została przekazana. Więc w tym sensie tak.
Czy możesz utworzyć parametr, którego wartości nie można przypisać w ramach metody lub którego właściwości nie można ustawić w ramach metody? Nie. Nie można zapobiec przypisywaniu wartości w metodzie, ale można zapobiec ustawianiu jej właściwości, tworząc niezmienny typ.
Nie chodzi o to, czy parametr lub jego właściwości można przypisać w ramach metody. Pytanie brzmi, co to będzie, gdy metoda zostanie zamknięta.
Jedynym przypadkiem, w którym jakiekolwiek zewnętrzne dane zostaną zmienione, jest przekazanie klasy i zmiana jednej z jej właściwości lub przekazanie wartości za pomocą słowa kluczowego ref. Sytuacja, którą nakreśliłeś, też nie.
źródło
x++
. Oznacza to, że próbuje zapobiec ponownemu przypisaniu parametru, co jest ortogonalne do zmienności wartości parametru.