Выделение универсальных типов.NET
Вот программа на C#, которая пытается Marshal.SizeOf
на несколько разных типов:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
class AClass { }
[StructLayout(LayoutKind.Sequential)]
struct AStruct { }
[StructLayout(LayoutKind.Sequential)]
class B { AClass value; }
[StructLayout(LayoutKind.Sequential)]
class C<T> { T value; }
class Program
{
static void M(object o) { Console.WriteLine(Marshal.SizeOf(o)); }
static void Main()
{
M(new AClass());
M(new AStruct());
M(new B());
M(new C<AStruct>());
M(new C<AClass>());
}
}
Первые четыре вызова M() завершаются успешно, но при последнем вызове SizeOf генерирует ArgumentException:
"Type 'C`1[AClass]' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."
Зачем? В частности, почему SizeOf подавляется C<AClass>
, но не на B
или на C<AStruct>
?
РЕДАКТИРОВАТЬ: Поскольку это было задано в комментариях, вот проблема "реального мира", которая вдохновила этот в основном академический вопрос: я обращаюсь к C API, который в основном является одной из функций C, которая работает (указывает на) много различные типы простых C-структур. Все они содержат общий заголовок, за которым следует одно поле, но тип этого поля отличается в разных структурах. Флаг в заголовке указывает тип поля. (Странно, да, но это то, с чем я должен работать).
Если бы я мог определить один общий тип C<T>
и единственное объявление C# extern M(C<T>)
, а затем позвоните M(C<int>)
на одной строке и M(C<double>)
с другой, у меня было бы короткое и приятное решение для взаимодействия. Но, учитывая ответ JaredPar, кажется, что я должен сделать отдельный тип C# для каждой структуры (хотя наследование может обеспечить общий заголовок).
2 ответа
Обобщения, как правило, не поддерживаются ни в одном сценарии взаимодействия. И P/Invoke, и COM Interop потерпят неудачу, если вы попытаетесь упорядочить универсальный тип или значение. Следовательно, я ожидал бы, что Marshal.SizeOf будет непроверенным или неподдерживаемым для этого сценария, поскольку это специфическая для маршала функция.
Неизвестно, какой размер будет иметь агрегированный объект T (это будет размер указателя, если T является ссылочным типом, и в основном любое значение, если это тип значения).
Я думаю, что вы можете решить эту проблему, установив атрибут MarshalAs в поле 'value', указав наиболее подходящий тип (например, Unmanagedtype.SysInt). Обратите внимание, что он все равно не будет работать для так называемых не отображаемых типов (т. Е. Типов, для которых смещения и размеры полей не могут быть легко определены).
Но AFAIK, не рекомендуется использовать дженерики во взаимодействии.