C#, const dla parametrów funkcji ?

C#, const dla parametrów funkcji ?
JK
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:55
0

Jak radzicie sobie z niemożnością użycia słowa kluczowego const podczas przekazywania parametrów do funkcji (w taki sposób w jaki można to wykonać w c++) ?

edytowany 1x, ostatnio: JeloneK
somekind
To w końcu o C++ czy o C# pytasz? Bo nie wiem, czy przenosić wątek czy nie.
JK
Wybacz, już edytowalem.
AreQrm
  • Rejestracja:około 11 lat
  • Ostatnio:2 miesiące
  • Lokalizacja:Londyn
  • Postów:873
0

Bardzo łatwo. Nie widzę do czego miało by mi to być potrzebne.
Napisz może o co Ci chodzi, co chcesz tym osiągnąć?


JK
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:55
0

Czasem użyteczne jest zagwarantowanie, że dany argument przekazany do funkcji nie zostanie zmieniony. Albo zagwarantowanie, że dana metoda nie zmieni pól obiektu. W c++ można to było osiągnąć poprzez użycie słowa "const". W C# nie widzę takiej możliwości ?

fasadin
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 3 lata
  • Postów:4882
1

ogolnie się nie da. Jest obejscie problemu... ale nie warto

http://stackoverflow.com/questions/3799062/const-methods-in-c-sharp

pamietaj C# to nie C++. Nie przenos ideologii jednego jezyka na drugi. Przewaznie to nie dziala.

Pixello
  • Rejestracja:około 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Podkarpacie
  • Postów:448
0

A może by tak zrobić deep copy tego co chcesz przekazać przed wywołaniem, albo nawet to w extensiona opakować.

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 8 godzin
0

w C# nie ma idei "const correctness". const używa się rzadko, do definiowania stałych.

JU
  • Rejestracja:około 22 lata
  • Ostatnio:2 miesiące
  • Postów:5042
0

OK, to teraz taka sytuacja. Metoda zwraca jakiś obiekt. Ale chcę, żeby nikt mi tego obiektu nie zmienił (w c++ to jest zwracanie stałej referencji). W C# jedyną opcją jest zwrócenie kopii tego obiektu?

Pixello
  • Rejestracja:około 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Podkarpacie
  • Postów:448
0

Obiekty anonimowe są całkowicie readonly, więc w tę stronę można iść jak się musi.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Wrocław
6

To nie jest jakiś duży problem, bo:

  1. W przypadku typów wartościowych takie zachowanie jest domyślne. Żeby móc zmienić w metodzie wartość parametru tak, aby była widziana poza nią, należałoby przekazać go jako out lub ref.
  2. W przypadku typów referencyjnych w metodzie nie podmienisz obiektu (no chyba, że użyjesz out albo ref).
  3. Jeśli o pola typów referencyjnych chodzi, to wystarczy, żeby klasa nie dawała możliwości zmiany takich pól.

Reasumując - aby osiągnąć to, czego potrzebujesz - wystarczy tak definiować klasy, aby obiekt po utworzeniu był niezmienny, oraz nie używać out i ref.

P.S. Stosowanie ref i out to w ogólności okropna praktyka prowadząca do powstawania syfiastego kodu.

JK
no ale funkcja może zwrocic tylko jedna wartosc, tymczasem czasami chcialbym np. przypisac (w jednej funkcji) do wiecej niz jednej zmiennej typu wartosciowego wartosc - czy w tym przypadku skorzystanie z out nie jest najlepszym rozwiazaniem ?
somekind
Nie, jest najgorszym z możliwych. Funkcja powinna zwracać jeden wynik za pomocą instrukcji return. Jeśli chcesz zwrócić kilka wartości z funkcji, to stwórz klasę która te wartości będzie zawierała i zwracaj obiekt tej klasy.
ŁF
@somekind: nie sądzę. Vide "zagwarantowanie, że dana metoda nie zmieni pól obiektu" - przekazujesz metodzie obiekt i nawet jeśli nie masz ref, to możesz z tym obiektem zrobić wszystko, na co Ci pozwoli, czyli np. zmienić wartości wszystkich pól z publicznymi setterami. Z kolei budowanie klasy w taki sposób, żeby na pewno żadna metoda nie zmieniła wartości pól jej instancji może być bardzo niewygodne.
somekind
@ŁF, może i niewygodne, za to bardzo obiektowe, a nawet prawie funkcyjne. W ten sposób unika się setek przyszłych błędów.
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 8 godzin
1

Ale chcę, żeby nikt mi tego obiektu nie zmienił

No cóż, w C# nie ma takiej obsesji w której programista broni się przed samym sobą, i pisząc funkcję foo() nie ufa sobie piszącemu funkcję bar() ;-)

JK
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:55
0

Przekazując do funkcji argument int[,] zapomnialem, ze to przeciez typ referencyjny! Przypisujac wiec wartosci do poszczegolnych elementow tablicy dzialalem nie na kopii, lecz na wlasciwym obiekcie. W tym momencie zaczalem wlasnie rozwazac uzycie slowa const, i stad moje pytanie.

JU
  • Rejestracja:około 22 lata
  • Ostatnio:2 miesiące
  • Postów:5042
0

Chodzi o to, że zwracam jakiś obiekt. Dajmy na to jakąś listę. I chcę mieć pewność, że tej listy mi nikt nie zmieni. Np. nie doda żadnego elementu, nie usunie, a nawet nie zmodyfikuje żadnego elementu listy. Np:

Kopiuj
 
List<MyObj> list = obj.GetList();
//i teraz bym chciał, żeby np. nie było możliwe zrobienie:
list.Add(new MyObjs());

//ale też bym chciał, żeby nie było możliwe:
list[0].OneProperty = newValue;
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Wrocław
4

@Juhas, jeśli chcesz, aby niemożliwa była modyfikacja kolekcji, to nie przekazujesz List<T>, która na to pozwala, lecz IEnumerable<T>. Jeśli nie chcesz modyfikować właściwości poszczególnych elementów, to uczyń je niemodyfikowalnymi, po prostu nie definiuj publicznych seterów.

edytowany 1x, ostatnio: somekind
JU
  • Rejestracja:około 22 lata
  • Ostatnio:2 miesiące
  • Postów:5042
0

No właśnie nie mogę nie robić publicznych setterów. Tzn. inaczej. Mam problem podobny do gościa, który pytał o ruchy królowej. Mam jedną klasę, która może modyfikować obiekty innej klasy. Ale inne klasy mogą jedynie odczytywać te obiekty. Ideałem byłoby c++ friend albo zwracanie obiektu przez stałą referencję. Innej możliwości nie widzę. No, pozostaje jeszcze Reflection, ale jednak wolałbym unikać takich fikołków.

edytowany 1x, ostatnio: Juhas
T9
Możesz spróbować zaimplementować 2 interfejsy jeden z samymi geterami, a drugi dziedziczący po pierwszym z samymi seterami explicit interface z publicznymi seterami. Klasy bez możliwości modyfikacji będą pobierać jako parametry ten piewrszy interfejs a te z możliwością ten drugi. Może nie najlepsze ale jakieś rozwiązanie.
T9
Mozesz też spróbować napisać klasy wewnątrz klasy wtedy, bo klasy 'dzieci' widzą prywatne metody klasy 'matki'. Jeśli uczynisz klase matke partial to będziesz mógł ją rozbić na wiele plików i każda klasa dziecko bedzie mogła mieć własny plik i nie bedzie bałąganu. Ale to takie kombinowane rozwiązanie.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Wrocław
0

Można też mieć różne zestawy obiektów - jeden mutowalny, używany przez te dwie klasy, które mogą mutować, drugi niemutowalny, dostępny dla reszty aplikacji.
Generalnie większość problemów idzie rozwiązać dobrą architekturą - może po prostu brakuje Ci jakiegoś wzorca, który pozwoliłby Ci tworzyć obiekty raz i nie trzeba byłoby ich modyfikować?

JU
  • Rejestracja:około 22 lata
  • Ostatnio:2 miesiące
  • Postów:5042
0

Nie, no właśnie potrzebuję je modyfikować, ale tylko w tej jednej klasie. A co z tymi zestawami obiektów? Znasz jakiś dobry wzorzec do tego? Bo póki co to moim rozwiązaniem jest zwracanie kopii takiej listy. Ale wiadomo, jakie to rozwiązanie, zwłaszcza jak lista jest duża.

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:4 dni
1

List<T>.AsReadOnly => ReadOnlyCollection<T>


JU
  • Rejestracja:około 22 lata
  • Ostatnio:2 miesiące
  • Postów:5042
0

OK, a jak uniemożliwić zmianę poszczególnych elementów kolekcji? (to nie są typy proste)

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:4 dni
0

A próbowałeś coś przypisać do elementu ReadOnlyCollection?


somekind
O ile dobrze rozumiem problem, to jemu chodzi o zmianę wartości jakiejś właściwości danego elementu listy, a nie zamianę całego elementu.
ŁF
Cóż, nie mój problem, że @Juhas wyraził się nieprecyzyjnie. Zmiana właściwości elementu listy nie ma nic wspólnego z listą samą w sobie, tylko z jej elementem, a to zostało przedyskutowane aż do bólu via argument metody.
0

jak wspomniano wcześniej - zamień elementy kolekcji na strukturę, lub zrób z nich obiekty niemutowalne (private sety)
ewentualnie do metody przekazuj klony lub elementy owrapowane (dekorator?) w obiekt readonly

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Wrocław
2
Juhas napisał(a):

Nie, no właśnie potrzebuję je modyfikować, ale tylko w tej jednej klasie. A co z tymi zestawami obiektów? Znasz jakiś dobry wzorzec do tego? Bo póki co to moim rozwiązaniem jest zwracanie kopii takiej listy. Ale wiadomo, jakie to rozwiązanie, zwłaszcza jak lista jest duża.

Jeśli zrobisz drugi zestaw obiektów, tak jak ja proponowałem, to i drugą listę będziesz musiał mieć.

Mi chodzi o coś takiego:

Kopiuj
class Student
{
    public string Name { get; set; }
}

class ImmutableStudent
{
    public string Name { get; private set; }

    public ImmutableStudent(Student s)
    {
        this.Name = s.Name;
    }
}

I jak już masz gotową kolekcję obiektów Student, z już poustawianymi wartościami, to tworzysz listę obiektów ImmutableStudent, i jej używa reszta klas w aplikacji. Innego sposobu nie znam.

ŁF napisał(a):

List<T>.AsReadOnly => ReadOnlyCollection<T>

Tylko trzeba pamiętać, że ReadOnlyCollection wcale nie jest takie znowu read only:

Kopiuj
var studentsList = new List<Student>()
{
    new Student { Name = "A" },
    new Student { Name = "B" },
    new Student { Name = "C" },
};

var readonlyStudents = studentsList.AsReadOnly();
studentsList.Add(new Student { Name = "readonly nie działa" });

foreach (var student in readonlyStudents)
{
    Console.WriteLine(student.Name);
}

Jak się chce mieć naprawdę niezmienną kolekcję, trzeba skorzystać z czegoś z przestrzeni System.Collections.Immutable - i trzeba to sobie ściągnąć z NuGeta, bo w standardowym frameworku tego nie ma.

Złoty Młot napisał(a):

jak wspomniano wcześniej - zamień elementy kolekcji na strukturę

Tylko bez przesady. Zamiana klasy na strukturę to generalnie jest zły pomysł. Strukury się stosuje w określonych przypadkach i taki typ musi spełniać określone warunki, żeby miał sens jako struktura.

T9
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 6 lat
  • Postów:329
1

Możesz spróbowac tak, jak wymyślisz jakieś sprytne dziedziczenie lub delegacje FriendClass(nie koniecznie słowem delegate ;) ) i fabryke by nie pisać tej śmiesznej podwójnek kropki tysiąc razy to to rozwiązanie może mieć ręce i nogi

Kopiuj
    
 class NormalClass 
    {
        public string Name{ get; protected set; } // by można było dziedziczyć po normal class
        public string Surname { get; protected set; }
        public class FriendClass
        {
            public void SetFrienName(NormalClass friend, string name)
            {
                friend.Name = name;
            }
            public void SetFrienSurname(NormalClass friend, string surname)
            {
                friend.Surname = surname;
            }
        }
    }

albo

Kopiuj
  
 class NormalClass 
    {
        private void DoSomething()
        public class FriendClass : NormalClass
        {
           public void Do()
           {
                 base.DoSomething();
           }
         }
    }  

Gdby friendClass i normal class miały długie implementacje mozesz zastosować słowo partial i zrobić każdą w osobnym pliku.
Jeśli to co zrobiłem wyżej zrobisz static, to bedziesz mógł napisać interface IFriend i do pisać do niego jako metode rozszerzeń SetFrienSurname, SetFrienName, a to pozwali Ci obdarzyć dowolną klase w programie implementującą IFriend dostępem do pól i metod prywatnych normalClass :D

ps pisalem wcześniej w komentarzu.

edytowany 9x, ostatnio: topik92

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.