Когда использовать указатели в C # /. NET? - PullRequest
65 голосов
/ 02 марта 2011

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

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

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

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

Ответы [ 5 ]

84 голосов
/ 02 марта 2011

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

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

Или, если вы человек, пишущий сортировочный слой.

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

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

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

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

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

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

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

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

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

17 голосов
/ 02 марта 2011

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

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

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

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

5 голосов
/ 02 марта 2011

GC может перемещать ссылки;использование unsafe удерживает объект вне контроля GC и избегает этого.«Фиксированный» закрепляет объект, но позволяет ГХ управлять памятью.

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

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

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

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

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

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

Что касается того, как они очищаются ... Вы должны управлять своей собственной памятью;объекты, на которые указывают ваши указатели, были созданы / размещены (обычно в C ++ DLL) с использованием (надеюсь) CoTaskMemAlloc(), и вы должны освободить эту память таким же образом, вызвав CoTaskMemFree(), иначе у вас будет утечка памяти,Обратите внимание, что с помощью CoTaskMemFree() может быть освобождена только память, выделенная с помощью CoTaskMemAlloc(). Другая альтернатива - предоставить метод из вашей родной библиотеки 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, который должен быть дополнен (после чего вступают в силу дальнейшие оптимизации)
  3. Вы не выключаете сборщик мусора ... вы просто скрываете объекты, с которыми работаете, от компетенции GC.Таким образом, каждые 100 мс или около того GC все еще прерывает ваш код и выполняет свои функции для всех других переменных в вашем управляемом коде.

HTH,Джеймс

3 голосов
/ 02 марта 2011

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

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

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

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

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

2 голосов
/ 28 января 2014

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

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

...