Я могу только вспомнить использование goto однажды. У меня была серия пяти вложенных считаемых циклов, и я должен был быть в состоянии убежать из всей структуры с внутренней части рано на основе определенных условий:
for{
for{
for{
for{
for{
if(stuff){
GOTO ENDOFLOOPS;
}
}
}
}
}
}
ENDOFLOOPS:
я, возможно, просто легко объявил булеву переменную повреждения и использовал ее в качестве части условного выражения для каждого цикла, но в этом экземпляре я решил, что GOTO был так же практичен и так же читаем.
Никакой velociraptors не напал на меня.
Является ли небезопасный код вариантом?
// allocate unmanaged memory
Foo* foo = (Foo*)Marshal.AllocHGlobal(sizeof(Foo));
// initialize struct
foo->bar = 0;
// invoke unmanaged function which remembers foo
UnsafeNativeMethods.Bar(foo);
Console.WriteLine(foo->bar);
// update struct
foo->bar = 10;
// invoke unmanaged function which uses remembered foo
UnsafeNativeMethods.Qux();
Console.WriteLine(foo->bar);
// free unmanaged memory
Marshal.FreeHGlobal((IntPtr)foo);
Это компилируется и не генерирует исключение, но у меня нет под рукой неуправляемой функции, чтобы проверить, работает ли она.
From MSDN :
Когда AllocHGlobal вызывает LocalAlloc, он передает флаг LMEM_FIXED, который приводит к блокировке выделенной памяти на месте. Кроме того, выделенная память не заполняется нулями.
Использование закрепленной памяти в этом случае не является хорошей идеей, учитывая, что память для структуры должна быть действительной в течение длительного времени. GCHandle.Alloc () закроет структуру и сохранит ее в куче. Когда он закреплен, сборщик мусора будет постоянно обременять его, поскольку ему необходимо постоянно находить обходной путь.
Простое решение - выделить память для структуры в неуправляемой памяти. Используйте Marshal.SizeOf (), чтобы получить размер структуры, и Marshal.AllocCoTaskMem (), чтобы выделить память. Это дает вам указатель, который нужно передать на неуправляемый код. Инициализируйте память с помощью Marshal.StructureToPtr (). И прочтите обновления структуры, написанные неуправляемым кодом, с помощью PtrToStructure ().
Если вы будете делать это часто, вы будете постоянно копировать структуру. Это может быть дорого, в зависимости от размеров конструкции. Чтобы этого избежать, используйте небезопасный указатель для прямого доступа к неуправляемой памяти. Базовый синтаксис:
using System;
using System.Runtime.InteropServices;
class Program {
unsafe static void Main(string[] args) {
int len = Marshal.SizeOf(typeof(Test));
IntPtr mem = Marshal.AllocCoTaskMem(len);
Test* ptr = (Test*)mem;
ptr->member1 = 42;
// call method
//..
int value = ptr->member1;
Marshal.FreeCoTaskMem(mem);
}
public struct Test {
public int member1;
}
}
Вместо закрепления вам необходимо использовать Marshal.StructureToPtr и Marshal.PtrToStructure для маршалинга структуры в память, которая может использоваться в машинном коде.
Пример структуры:
[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED_STRUCT
{
public IntPtr InternalLow;
public IntPtr InternalHigh;
public Int32 OffsetLow;
public Int32 OffsetHigh;
public IntPtr EventHandle;
}
Как закрепить ее в структуре и использовать:
OVERLAPPED_STRUCT over_lapped = new OVERLAPPED_STRUCT();
// edit struct in managed code
over_lapped.OffsetLow = 100;
IntPtr pinned_overlap_struct = Marshal.AllocHGlobal(Marshal.SizeOf(over_lapped));
Marshal.StructureToPtr(over_lapped, pinned_overlap_struct, true);
// Pass pinned_overlap_struct to your unmanaged code
// pinned_overlap_struct changes ...
// Get resulting new struct
OVERLAPPED_STRUCT nat_ov = (OVERLAPPED_STRUCT)Marshal.PtrToStructure(pinned_overlap_struct, typeof(OVERLAPPED_STRUCT));
// See what new value is
int offset_low = nat_ov.OffsetLow;
// Clean up
Marshal.FreeHGlobal(pinned_overlap_struct);