Kiedy używać wskaźników w C#/. Net?

Wiem, że C# daje programiście możliwość dostępu, używania wskaźników w niebezpiecznym kontekście. Ale kiedy jest to potrzebne?

W jakich okolicznościach używanie wskaźników staje się nieuniknione?

Czy to tylko ze względów wydajnościowych?

Również dlaczego C# ujawnia tę funkcjonalność poprzez niebezpieczny kontekst i usuwa z niej wszystkie zarządzane zalety? Czy możliwe jest korzystanie ze wskaźników bez utraty zalet zarządzanego środowiska, teoretycznie?

Author: Joan Venge, 2011-03-02

5 answers

Kiedy jest to potrzebne? W jakich okolicznościach używanie wskaźników staje się nieuniknione?

Gdy koszt netto zarządzanego, bezpiecznego rozwiązania jest niedopuszczalny, ale koszt netto niebezpiecznego rozwiązania jest akceptowalny. Możesz określić koszt netto lub korzyść netto, odejmując całkowite korzyści od całkowitych kosztów. Korzyści płynące z niebezpiecznego rozwiązania to "brak czasu zmarnowanego na niepotrzebne kontrole w celu zapewnienia poprawności"; koszty to (1) konieczność pisania kodu jest to bezpieczne nawet przy wyłączonym zarządzanym systemie bezpieczeństwa i (2) konieczności radzenia sobie z potencjalnym zmniejszeniem wydajności garbage collector, ponieważ nie może poruszać się po pamięci z niezarządzanym wskaźnikiem.

Lub, jeśli jesteś osobą piszącą warstwę.

Czy to tylko ze względów wydajnościowych?

Przewrotne wydaje się używanie wskaźników w języku zarządzanym z powodów innych niż wydajność.

Możesz użyć metod w Klasa Marszałkowska zajmująca się współdziałaniem z niezarządzanym kodem w zdecydowanej większości przypadków. (Może być kilka przypadków, w których użycie sprzętu rozrządowego jest trudne lub niemożliwe do rozwiązania problemu interop, ale nie wiem o żadnym.)

Oczywiście, jak powiedziałem, jeśli jesteś osobą piszącą klasę Marshal, to oczywiście nie dostajesz się do użyj warstwy marshalling, aby rozwiązać swój problem. W takim przypadku musisz zaimplementować go za pomocą wskaźników.

Dlaczego czy C# ujawnia tę funkcjonalność poprzez niebezpieczny kontekst i usuwa z niej wszystkie zarządzane zalety?

Te zarządzane korzyści wiążą się z kosztami wydajności. Na przykład, za każdym razem, gdy zapytasz tablicę o jej dziesiąty element, runtime musi sprawdzić, czy nie ma dziesiątego elementu, i rzucić wyjątek, jeśli nie ma. ze wskaźnikami, że koszt runtime jest wyeliminowany.

Odpowiedni koszt dewelopera polega na tym, że jeśli robisz to źle, to masz do czynienia z błędy korupcji pamięci, które formatują dysk twardy i zawiesza proces godzinę później, zamiast radzić sobie z ładnym czystym wyjątkiem w punkcie błędu.

Czy możliwe jest używanie wskaźników, nie tracąc teoretycznie żadnych zalet zarządzanego środowiska?

Przez "zalety" zakładam, że masz na myśli zalety, takie jak wywóz śmieci, bezpieczeństwo typu i integralność odniesienia. Zatem twoje pytanie brzmi zasadniczo " czy teoretycznie możliwe jest Wyłączenie bezpieczeństwa system, ale nadal korzystać z zalet systemu bezpieczeństwa jest włączony?"Nie, najwyraźniej nie jest. Jeśli wyłączysz ten system bezpieczeństwa, ponieważ nie podoba Ci się, jak drogi jest, to nie dostaniesz korzyści z tego, że jest włączony!

 78
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-02 19:36:29

Wskaźniki są nieodłączną sprzecznością z zarządzanym, zbieranym, środowiskiem.
Gdy zaczniesz mieszać z surowymi wskaźnikami, GC nie ma pojęcia, co się dzieje.

W szczególności, nie może stwierdzić, czy obiekty są osiągalne, ponieważ nie wie, gdzie są Twoje wskaźniki.
Nie może również przenosić obiektów w pamięci, ponieważ złamałoby to wskaźniki.

Wszystko to zostałoby rozwiązane za pomocą wskaźników śledzonych przez GC; tym są odniesienia.

Powinieneś używaj wskaźników tylko w zaawansowanych scenariuszach interop lub do wysoce wyrafinowanej optymalizacji.
Jeśli musisz pytać, prawdopodobnie nie powinieneś.

 15
Author: SLaks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-02 19:01:52

GC może przenosić odwołania; używanie unsafe utrzymuje obiekt poza kontrolą GC i unika tego. "Fixed" przypina obiekt, ale pozwala GC zarządzać pamięcią.

Z definicji, jeśli masz wskaźnik na adres obiektu, a GC go przesuwa, twój wskaźnik nie jest już ważny.

Dlaczego potrzebujesz wskaźników: głównym powodem jest praca z niezarządzanymi bibliotekami DLL, np. tymi napisanymi w C++

Zauważ również, że gdy przypinasz zmienne i używasz wskaźników, jesteś bardziej podatne na rozdrobnienie sterty.


Edit

Poruszyłeś główny problem kodu zarządzanego i niezarządzanego... jak zwalnia się pamięć?

Możesz mieszać kod dla wydajności, tak jak opisujesz, po prostu nie możesz przekraczać zarządzanych/niezarządzanych granic ze wskaźnikami (tzn. nie możesz używać wskaźników poza kontekstem "niebezpiecznym").

Jeśli chodzi o sposób ich czyszczenia... Musisz zarządzać własną pamięcią; obiekty, na które wskazują Twoje wskaźniki, były utworzone/przydzielone (zwykle w C++ DLL) za pomocą (miejmy nadzieję) CoTaskMemAlloc(), i musisz zwolnić tę pamięć w ten sam sposób, wywołując CoTaskMemFree(), albo będziesz miał wyciek pamięci. Zauważ, że tylko pamięć przydzielona przez CoTaskMemAlloc() może zostać zwolniona przez CoTaskMemFree().

Inną alternatywą jest ujawnienie metody z natywnej biblioteki C++, która pobiera wskaźnik i uwalnia go... to pozwala DLL zdecydować, jak zwolnić pamięć, która działa najlepiej, jeśli użył innej metody alokacji pamięci. Większość rodzimych bibliotek DLL praca z bibliotekami DLL innych firm, których nie można modyfikować, i zwykle nie mają (że widziałem) takich funkcji do wywołania.

Przykład uwolnienia pamięci, zaczerpnięty z tutaj :

string[] array = new string[2];
array[0] = "hello";
array[1] = "world";
IntPtr ptr = test(array);
string result = Marshal.PtrToStringAuto(ptr);
Marshal.FreeCoTaskMem(ptr);
System.Console.WriteLine(result);


Więcej materiałów do czytania:

C # deallocate memory referred by IntPtr Druga odpowiedź wyjaśnia różne metody alokacji / dealokacji

Jak uwolnić IntPtr w C#? Wzmacnia potrzebę dealokacji w ten sam sposób pamięć została przydzielona

Http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx Oficjalna dokumentacja MSDN dotycząca różnych sposobów alokacji i dealokacji pamięci.

W skrócie... musisz wiedzieć, jak przydzielono pamięć, aby ją uwolnić.


Edit Jeśli dobrze rozumiem twoje pytanie, krótka odpowiedź brzmi tak, możesz przekazać dane do niezarządzanych wskaźników, pracować z nimi w niebezpiecznym kontekście i mieć dane dostępne raz wychodzisz z niebezpiecznego kontekstu.

Klucz polega na tym, że musisz przypiąć zarządzany obiekt, do którego się odnosisz, za pomocą bloku fixed. Dzięki temu pamięć, do której się odwołujesz, nie jest przenoszona przez GC w bloku unsafe. Istnieje tu wiele subtelności, np. nie można ponownie przypisać wskaźnika zainicjowanego w stałym bloku... powinieneś przeczytać o niebezpiecznych i stałych instrukcjach, jeśli naprawdę jesteś nastawiony na zarządzanie własnym kodem.

Wszystko to powiedział, korzyści z zarządzania swoim własne obiekty i używanie wskaźników w sposób, który opisujesz, może nie kupić ci tak dużego wzrostu wydajności, jak mogłoby ci się wydawać. Powody dlaczego nie:

  1. C # jest bardzo zoptymalizowany i bardzo szybki
  2. Twój kod wskaźnika jest nadal generowany jako IL, który musi być jitted (w którym momencie wchodzą dalsze optymalizacje)
  3. Nie wyłączysz śmieciarza... po prostu trzymasz przedmioty, z którymi pracujesz poza zasięgiem GC. Więc każdy Około 100ms, GC nadal przerywa kod i wykonuje jego funkcje dla wszystkich innych zmiennych w zarządzanym kodzie.

HTH,
James

 4
Author: James King,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:50

Najczęstsze powody używania wskaźników jawnie w C#:

  • praca na niskim poziomie (jak manipulacja strunami), która jest bardzo wrażliwa na wydajność,
  • [[3]}interfejs z niezarządzanymi interfejsami API.

Powód, dla którego składnia związana ze wskaźnikami została usunięta z C# (zgodnie z moją wiedzą i poglądem-Jon Skeet lepiej odpowie B -)) okazał się zbędny w większości sytuacji.

Z punktu widzenia projektowania języka, po zarządzaniu pamięć przez garbage collector musisz wprowadzić poważne ograniczenia dotyczące tego, co jest, a czego nie można zrobić ze wskaźnikami. Na przykład użycie wskaźnika do wskazywania środka obiektu może spowodować poważne problemy dla GC. W związku z tym, po wprowadzeniu ograniczeń, możesz po prostu pominąć dodatkową składnię i skończyć z "automatycznymi" odwołaniami.

Ponadto, ultra-życzliwe podejście Znalezione w C / C++ jest częstym źródłem błędów. W większości sytuacji, w których mikro-wydajność nie materia w ogóle, lepiej jest zaoferować bardziej rygorystyczne zasady i ograniczyć dewelopera na rzecz mniej błędów, które byłyby bardzo trudne do odkrycia. Tak więc dla zwykłych aplikacji biznesowych tak zwane "zarządzane" środowiska, takie jak. NET i Java, są lepiej dostosowane niż języki, które zakładają, że działają przeciwko maszynie bare-metal.

 2
Author: Ondrej Tucny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-02 18:50:41

Powiedz, że chcesz komunikować się między 2 aplikacjami za pomocą IPC (shared memory), a następnie możesz przenieść dane do pamięci i przekazać ten wskaźnik danych do innej aplikacji za pośrednictwem wiadomości windows lub czegoś takiego. Przy odbiorze aplikacji można pobrać dane z powrotem.

Przydatne również w przypadku przesyłania danych z. NET do starszych aplikacji VB6, w którym dane zostaną przeniesione do pamięci, przekazać wskaźnik do aplikacji VB6 za pomocą win msging, użyć VB6 copymemory (), aby pobrać dane z zarządzanej przestrzeni pamięci do Aplikacje VB6 niezarządzana przestrzeń pamięci..

 1
Author: variable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 04:38:51