To bardzo dobre pytanie, ponieważ widziałem wielu programistów C ++ piszących okropny kod C #.
Najlepiej nie myśleć o C # jako „C ++ z ładniejszą składnią”. Są to różne języki, które wymagają odmiennego podejścia do myślenia. C ++ zmusza cię do ciągłego myślenia o tym, co zrobi procesor i pamięć. C # nie jest taki. C # jest specjalnie zaprojektowany, abyś nie myślał o procesorze i pamięci, a zamiast tego myślisz o domenie biznesowej, dla której piszesz .
Jednym z przykładów tego, co widziałem, jest to, że wielu programistów C ++ lubi używać pętli, ponieważ są szybsze niż foreach. Jest to zwykle zły pomysł w języku C #, ponieważ ogranicza możliwe typy iteracji kolekcji (a tym samym możliwość ponownego użycia i elastyczność kodu).
Myślę, że najlepszym sposobem na dostosowanie od C ++ do C # jest próba podejścia do kodowania z innej perspektywy. Na początku będzie to trudne, ponieważ z biegiem lat będziesz przyzwyczajony do używania w mózgu wątku „co robi procesor i pamięć” do filtrowania pisanego kodu. Ale w języku C # powinieneś zamiast tego myśleć o relacjach między obiektami w domenie biznesowej. „Co chcę robić”, a nie „co robi komputer”.
Jeśli chcesz coś zrobić na liście obiektów, zamiast pisać na liście pętlę for, utwórz metodę, która pobiera pętlę IEnumerable<MyObject>
i używa foreach
.
Twoje konkretne przykłady:
Kontrolowanie czasu życia zasobów, które wymagają deterministycznego czyszczenia (np. Plików). Łatwo jest to wykorzystać, ale jak właściwie z niego korzystać, gdy własność zasobu jest przenoszona [... między wątkami]? W C ++ po prostu używałbym wspólnych wskaźników i pozwalał zająć się „odśmiecaniem” w odpowiednim czasie.
Nigdy nie powinieneś tego robić w języku C # (szczególnie między wątkami). Jeśli chcesz coś zrobić z plikiem, zrób to w jednym miejscu na raz. Jest ok (i rzeczywiście dobra praktyka), aby napisać klasę opakowania, która zarządza niezarządzanym zasobem, który jest przekazywany między twoimi klasami, ale nie próbuj przekazywać pliku między wątkami i mieć osobne klasy, które zapisują / czytają / zamykają / otwierają. Nie dziel się własnością niezarządzanych zasobów. Użyj Dispose
wzoru, aby poradzić sobie z porządkiem.
Ciągłe zmaganie się z nadpisywaniem funkcji dla określonych generycznych (uwielbiam takie rzeczy, jak częściowa specjalizacja szablonów w C ++). Czy powinienem po prostu zrezygnować z jakichkolwiek prób programowania ogólnego w języku C #? Może generyczne są celowo ograniczone i używanie ich nie jest C # poza konkretną domeną problemów?
Ogólne w C # są zaprojektowane, aby być ogólne. Specjalizacje generyczne powinny być obsługiwane przez klasy pochodne. Dlaczego warto List<T>
zachowywać się inaczej, jeśli jest to List<int>
a List<string>
? Wszystkie operacje na List<T>
są ogólne, aby można je było zastosować do dowolnej List<T>
. Jeśli chcesz zmienić zachowanie .Add
metody na List<string>
, a następnie utwórz klasę pochodną MySpecializedStringCollection : List<string>
lub klasę złożoną, MySpecializedStringCollection : IList<string>
która korzysta z tego, co ogólne, ale robi różne rzeczy. Pomoże to uniknąć naruszenia zasady substytucji Liskowa i po prostu rujnować innych, którzy korzystają z twojej klasy.
Funkcjonalność przypominająca makro. Chociaż ogólnie jest to zły pomysł, w przypadku niektórych domen problemów nie ma innego obejścia (np. Warunkowa ocena instrukcji, podobnie jak w przypadku dzienników, które powinny przejść tylko do wersji debugowania). Brak ich oznacza, że muszę postawić więcej, jeśli (warunek) {...} płyta kotłowa i nadal nie jest równa pod względem wywoływania efektów ubocznych.
Jak powiedzieli inni, możesz to zrobić za pomocą poleceń preprocesora. Atrybuty są jeszcze lepsze. Ogólnie atrybuty są najlepszym sposobem obsługi rzeczy, które nie są funkcjami „rdzenia” dla klasy.
Krótko mówiąc, pisząc C #, pamiętaj, że powinieneś myśleć wyłącznie o domenie biznesowej, a nie o procesorze i pamięci. W razie potrzeby możesz zoptymalizować później , ale kod powinien odzwierciedlać relacje między zasadami biznesowymi, które próbujesz zmapować.
C#
do wygenerowania innego kodu. Możesz odczytać plikCSV
lubXML
plik, który zapisałeś jako dane wejściowe i wygenerować plikC#
lubSQL
plik. Może to być bardziej wydajne niż używanie makr funkcjonalnych.