Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Или, если вы являетесь человеком, который пишет маршаллирующий слой.

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

Все это было бы решено GC-гусеничными указателями; что какие ссылки есть.

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

Ответ 3

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

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

Что вам нужно для указателей: Основная причина - работать с неуправляемыми DLL, например. написанные на С++

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


Edit

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

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

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

Другая альтернатива заключается в том, чтобы выставить метод из вашей родной С++ dll, который принимает указатель и освобождает его... это позволяет 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);


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

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

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

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

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


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

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

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

  • С# очень оптимизирован и очень быстро
  • Ваш код указателя по-прежнему генерируется как IL, который должен быть помечен (в этот момент вступает в игру дальнейшая оптимизация)
  • Вы не отключаете сборщик мусора... вы просто держите объекты, с которыми работаете, вне поля зрения GC. Поэтому каждые 100 мс GC еще прерывает ваш код и выполняет его функции для всех других переменных в управляемом коде.

HTH,
Джеймс

Ответ 4

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

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

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

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

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

Ответ 5

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

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