Править: Я хорошо знаю об этом, это работает очень хорошо с типами значения, мой конкретный вопрос об использовании этого для ссылочных типов.
Edit2: я также знаю, что Вы не можете наложить ссылочные типы и оценить типы в структуре, это только для случая накладывания нескольких полей ссылочного типа друг с другом.
Я был несерьезен вокруг со структурами в.NET/C#, и я просто узнал, что можно сделать это:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1 {
class Foo { }
class Bar { }
[StructLayout(LayoutKind.Explicit)]
struct Overlaid {
[FieldOffset(0)] public object AsObject;
[FieldOffset(0)] public Foo AsFoo;
[FieldOffset(0)] public Bar AsBar;
}
class Program {
static void Main(string[] args) {
var overlaid = new Overlaid();
overlaid.AsObject = new Bar();
Console.WriteLine(overlaid.AsBar);
overlaid.AsObject = new Foo();
Console.WriteLine(overlaid.AsFoo);
Console.ReadLine();
}
}
}
В основном обходя имеющий необходимость сделать динамический кастинг во время времени выполнения при помощи структуры, которая имеет явное полевое расположение и затем доступ к объекту внутри, поскольку это - корректный тип.
Теперь мой вопрос: это может привести к утечкам памяти так или иначе или неопределенному поведению в CLR? Или действительно ли это - полностью поддерживаемая конвенция, которая применима без каких-либо проблем?
Я знаю, что это - один из более темных углов CLR, и что эта техника является только жизнеспособным вариантом в очень немногих конкретных случаях.
Я не могу понять, как версию с явным макетом можно проверить без дополнительных проверок во время выполнения , поскольку это позволяет вам чтобы увидеть ненулевую ссылку на то, что не относится к объявленному типу.
Это будет безопаснее:
struct Overlaid { // could also be a class for reference-type semantics
private object asObject;
public object AsObject {get {return asObject;} set {asObject = value;} }
public Foo AsFoo { get {return asObject as Foo;} set {asObject = value;} }
public Bar AsBar { get {return asObject as Bar;} set {asObject = value;} }
}
Нет риска разорвать ссылки и т. Д., И все равно будет только одно поле. Он не включает в себя какой-либо рискованный код и т. Д. В частности, он не рискует чем-то глупым, например:
[FieldOffset(0)]
public object AsObject;
[FieldOffset(0)]
public Foo AsFoo;
[FieldOffset(1)]
public Bar AsBar; // kaboom!!!!
Другая проблема заключается в том, что вы можете поддерживать только одно поле таким образом, если вы не можете гарантировать режим ЦП; offset 0 - это просто, но становится сложнее, если вам нужно несколько полей и нужно поддерживать x86 и x64.
Если вы выровняете тип небезопасным способом, среда выполнения выдаст исключение TypeLoadException
при загрузке даже при компиляции с / unsafe
. Так что я думаю, что ты в безопасности.
Я предполагаю - поскольку вы можете использовать StructLayout
и скомпилировать код без флагов / unsafe
), что это особенность среды CLR. Атрибут StructLayout нужен просто потому, что в C # нет прямых средств для такого объявления типов.
Взгляните на эту страницу , где подробно описаны некоторые способы преобразования структур C # в IL. Вы заметите, что существует множество макетов памяти, встроенных в сам IL / CLR.
Что ж, вы обнаружили дыру в петле, CLR разрешает это, поскольку все перекрывающиеся поля объекты. Все, что позволит вам вмешиваться в объектную ссылку, напрямую отклоняется с помощью TypeLoadException:
[StructLayout(LayoutKind.Explicit)]
struct Overlaid {
[FieldOffset(0)]
public object AsObject;
[FieldOffset(0)]
public IntPtr AsPointer;
}
Но вы можете использовать это, задав поля классов. Ничего плохого не происходит, пока вы просто читаете значения полей, например, вы можете получить значение дескриптора отслеживания.
Однако запись этих полей приводит к исключению ExecutionEngineException. Однако я думаю, что это эксплойт, если вы можете правильно угадать значение дескриптора отслеживания. Однако практическое использование достаточно близко к нулю.
Мне не известно о каких-либо проблемах с ним. Более того, я сомневаюсь, что Microsoft разрешила бы такое использование, если бы оно было неочевидным образом опасным.