Когда использовать указатели в C# / .NET?

Я знаю, что C# дает программисту возможность доступа, использования указателей в небезопасном контексте. Но когда это нужно?

при каких обстоятельствах использование указателей становится неизбежным?

Это только по соображениям производительности?

также почему C# предоставляет эту функциональность через небезопасный контекст и удаляет из него все управляемые преимущества? Можно ли использовать указатели без потери каких-либо преимуществ управляемой среде, теоретически?

5 ответов


когда это необходимо? При каких обстоятельствах использование указателей становится неизбежным?

когда чистая стоимость управляемого, безопасного решения неприемлема, но чистая стоимость небезопасного решения приемлема. Можно определить чистую стоимость или чистую выгоду, вычитая общие выгоды из общих затрат. Преимущества небезопасного решения-это такие вещи, как "нет времени на ненужные проверки времени выполнения для обеспечения правильности"; затраты (1) имеют писать код, который безопасен даже при отключенной управляемой системе безопасности, и (2) иметь дело с потенциальным снижением эффективности сборщика мусора, потому что он не может перемещаться по памяти с неуправляемым указателем.

или, если вы человек, пишущий слой маршалинга.

это только по соображениям производительности?

кажется неправильным использовать указатели на управляемом языке по причинам, отличным от производительности.

вы можете использовать методы класса Marshal для взаимодействия с неуправляемым кодом в подавляющем большинстве случаев. (Может быть, есть несколько случаев, когда трудно или невозможно использовать маршалинг для решения проблемы взаимодействия, но я не знаю ни одного.)

конечно, как я уже сказал, Если вы человек, пишущий класс Маршала, то, очевидно, вы не получите использовать уровень маршалинга для решения вашей проблемы. В таком случае ... нужно реализовать его с помощью указателей.

почему C# предоставляет эту функциональность через небезопасный контекст и удаляет из него все управляемые преимущества?

те управляемые преимущества приходят с ценами представления. Например, каждый раз, когда вы запрашиваете массив для его десятого элемента, среда выполнения должна проверить, есть ли десятый элемент, и создать исключение, если его нет. С указателями, что стоимость выполнения устраненный.

соответствующая стоимость разработчика заключается в том, что если вы сделаете это неправильно, то вы получите дело с ошибками повреждения памяти, которые форматируют ваш жесткий диск и сбой процесса через час, а не иметь дело с хорошим чистым исключением в точке ошибки.

можно ли использовать указатели без потери каких-либо преимуществ управляемой среде, теоретически?

под "преимуществами" я предполагаю, что вы имеете в виду преимущества, такие как мусор коллекция, тип безопасности и целостности данных. Таким образом, ваш вопрос по существу: "теоретически возможно отключить систему безопасности, но все же получить преимущества включения системы безопасности?- Нет, это явно не так. Если вы выключите эту систему безопасности, потому что вам не нравится, насколько она дорогая, вы не получите преимуществ от ее включения!


указатели являются неотъемлемым противоречием управляемой, собираемой мусором среде.
Как только вы начнете возиться с необработанными указателями, GC не имеет понятия, что происходит.

в частности, он не может сказать, доступны ли объекты, так как он не знает, где находятся ваши указатели.
Он также не может перемещать объекты в памяти, так как это нарушит ваши указатели.

все это будет решаться с помощью GC-отслеживаемых указателей; вот какие ссылки являются.

вы должны использовать указатели только в грязных расширенных сценариях взаимодействия или для очень сложной оптимизации.
Если вы должны спросить, вы, вероятно, не должны.


GC может перемещать ссылки; использование unsafe сохраняет объект вне контроля GC и избегает этого. "Исправлено" закрепляет объект, но позволяет GC управлять памятью.

по определению, если у вас есть указатель на адрес объекта и сборщик мусора перемещает его, указатель больше не действителен.

Что касается того, почему вам нужны указатели: основная причина-работать с неуправляемыми DLL, например, написанными на C++

Также обратите внимание, когда вы закрепляете переменные и использовать указатели, вы более восприимчивы к фрагментации кучи.


редактировать

вы коснулись основной проблемы управляемого и неуправляемого кода... как высвобождается память?

вы можете смешивать код для производительности, как вы описываете, вы просто не можете пересекать управляемые/неуправляемые границы с указателями (т. е. вы не можете использовать указатели вне "небезопасного" контекста).

Что касается того, как они очищаются... Вы должны управлять своими собственными объекты, на которые указывают ваши указатели, были созданы / выделены (обычно в C++ DLL) с помощью (надеюсь) CoTaskMemAlloc(), и вы должны освободить эту память таким же образом, вызывая CoTaskMemFree(), или у вас будет утечка памяти. Обратите внимание, что только память, выделенная с CoTaskMemAlloc() можно освободить с помощью CoTaskMemFree().

другой альтернативой является предоставление метода из вашей родной dll C++, который берет указатель и освобождает его... это позволяет DLL решить, как освободить память, которая работает лучше всего, если она использовал какой-то другой метод выделения памяти. Большинство собственных библиотек DLL, с которыми вы работаете,-это сторонние библиотеки DLL, которые вы не можете изменить, и у них обычно нет (что я видел) таких функций для вызова.

пример освобождения памяти, взятый из здесь:

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);


Еще несколько материалов для чтения:

C# освобождает память, на которую ссылается IntPtr Второй ответ вниз объясняет различное распределение/освобождение методы

как освободить IntPtr в C#? Усиливает необходимость освобождения таким же образом, как была выделена память

http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx Официальная документация MSDN о различных способах выделения и освобождения памяти.

короче... вам нужно знать, как была выделена память, чтобы освободить ее.


редактировать Если я понимаю ваше вопрос правильно, короткий ответ-да, вы можете передать данные неуправляемым указателям, работать с ними в небезопасном контексте и иметь данные, доступные после выхода из небезопасного контекста.

ключ заключается в том, что вы должны закрепить управляемый объект, на который вы ссылаетесь, с помощью fixed блок. Это предотвращает перемещение памяти, на которую вы ссылаетесь, GC во время unsafe блок. Здесь задействован ряд тонкостей, например, вы не можете переназначить инициализированный указатель в стационарном блоке... вы должны прочитать о небезопасных и фиксированных операторах, если вы действительно настроены на управление собственным кодом.

все это говорит о том, что преимущества управления собственными объектами и использования указателей таким образом, как вы описываете, не могут купить вам столько повышения производительности, как вы могли бы подумать. Причины, почему нет:

  1. C# очень оптимизирован и очень быстрый
  2. ваш код указателя по-прежнему генерируется как IL, который должен быть jitted (при котором точки дальнейшей оптимизации вступают в игру)
  3. вы не выключаете сборщик мусора... вы просто держите объекты, с которыми вы работаете, вне компетенции GC. Так что каждые 100ms или около того, GC еще прерывает код и выполняет его функции для всех других переменных в управляемом коде.

HTH,
Джеймс!--8-->


наиболее распространенные причины использования указателей явно в C#:

  • выполнение низкоуровневой работы (например, манипуляция строками), которая очень чувствительна к производительности,
  • взаимодействие с неуправляемыми API.

причина, по которой синтаксис, связанный с указателями, был удален из C# (согласно моим знаниям и точке зрения - Джон Скит ответил бы лучше B -)), оказалась излишней в большинстве ситуаций.

из языкового дизайна перспектива, как только вы управляете памятью сборщиком мусора, вы должны ввести серьезные ограничения на то, что есть и что невозможно сделать с указателями. Например, использование указателя для указания на середину объекта может вызвать серьезные проблемы для GC. Следовательно, как только ограничения установлены, Вы можете просто опустить дополнительный синтаксис и получить "автоматические" ссылки.

кроме того, ультра-благожелательный подход, найденный в C/C++, является общим источником ошибок. Для большинства ситуаций, там, где микро-производительность не имеет значения, лучше предложить более жесткие правила и ограничить разработчика в пользу меньшего количества ошибок, которые было бы очень трудно обнаружить. Таким образом, для обычных бизнес-приложений так называемые "управляемые" среды, такие как .NET и Java, лучше подходят, чем языки, которые предполагают работать против машины с голым металлом.


скажем, вы хотите общаться между 2 приложения с помощью IPC (общая память), то вы можете маршалировать данные в память и передать этот указатель данных в другое приложение через Windows messaging или что-то. При получении приложения вы можете получить данные обратно.

полезно также в случае передачи данных из .NET в устаревшие приложения VB6, где вы будете маршалировать данные в память, передавать указатель на приложение VB6 с помощью win msging, использовать VB6 copymemory() для извлечения данных из управляемой памяти пространство в VB6 приложения неуправляемой памяти..