Накладывание нескольких полей ссылки CLR друг с другом в явной структуре?

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

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, и что эта техника является только жизнеспособным вариантом в очень немногих конкретных случаях.

11
задан thr 24 April 2010 в 07:34
поделиться

4 ответа

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

Это будет безопаснее:

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.

2
ответ дан 3 December 2019 в 11:03
поделиться

Если вы выровняете тип небезопасным способом, среда выполнения выдаст исключение TypeLoadException при загрузке даже при компиляции с / unsafe . Так что я думаю, что ты в безопасности.

Я предполагаю - поскольку вы можете использовать StructLayout и скомпилировать код без флагов / unsafe ), что это особенность среды CLR. Атрибут StructLayout нужен просто потому, что в C # нет прямых средств для такого объявления типов.

Взгляните на эту страницу , где подробно описаны некоторые способы преобразования структур C # в IL. Вы заметите, что существует множество макетов памяти, встроенных в сам IL / CLR.

2
ответ дан 3 December 2019 в 11:03
поделиться

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

  [StructLayout(LayoutKind.Explicit)]
  struct Overlaid {
    [FieldOffset(0)]
    public object AsObject;
    [FieldOffset(0)]
    public IntPtr AsPointer;
  }

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

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

3
ответ дан 3 December 2019 в 11:03
поделиться

Мне не известно о каких-либо проблемах с ним. Более того, я сомневаюсь, что Microsoft разрешила бы такое использование, если бы оно было неочевидным образом опасным.

-1
ответ дан 3 December 2019 в 11:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: