2009-10-16 24 views
7

Aquí es un programa C# que intenta Marshal.SizeOf en unos pocos tipos diferentes:Marshalling .NET tipos genéricos

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>()); 
    } 
} 

Los primeros cuatro llamadas a M() tener éxito, pero en el último, SizeOf lanza una excepción ArgumentException:

"Type 'C`1[AClass]' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed." 

¿Por qué? Específicamente, ¿por qué SizeOf se estrangula en C<AClass>, pero no en B o en C<AStruct>?


EDIT: Debido a que se le preguntó acerca de los comentarios, aquí está el problema del "mundo real" que inspiró a esta pregunta en su mayoría-académica: Voy a llamar a una API de C, que es básicamente un C función que opera en (punteros a) muchos tipos diferentes de estructuras C simples. Todos contienen un encabezado común seguido de un campo, pero el tipo de ese campo es diferente en diferentes estructuras. Una bandera en el encabezado indica el tipo de campo. (Extraño, sí, pero eso es con lo que tengo que trabajar).

Si pudiera definir un único tipo genérico C<T> y una sola C# M(C<T>) declaración externa, y luego llamar a M(C<int>) en una línea, y M(C<double>) en otro, tendría una solución de interoperabilidad corto y dulce. Pero dada la respuesta de JaredPar, parece que tengo que hacer un tipo de C# por separado para cada estructura (aunque la herencia puede proporcionar el encabezado común).

+0

¿Qué excepción se produce? –

+0

ArgumentException, con el mensaje que pegó. –

+0

@Philipp: acaba de ser editado para aclarar eso, gracias. – Gabriel

Respuesta

7

Generalmente, los genéricos no se admiten en ningún escenario de interoperabilidad. Tanto P/Invoke como COM Interop fallarán si intenta calcular un tipo o valor genérico. Por lo tanto, esperaría que Marshal.SizeOf no se haya probado o no esté soportado para este escenario, ya que es una función específica del agente.

0

No se sabe qué tamaño tendría el objeto agregado T (sería el tamaño de un puntero si T es un tipo de referencia y sobre todo cualquier valor si es de tipo de valor).

Creo que puede resolver este problema estableciendo el atributo MarshalAs en el campo 'valor' que especifica el tipo más coincidente (por ejemplo, Unmanagedtype.SysInt). Tenga en cuenta que aún no funcionará para los llamados tipos no asignables (es decir, tipos para los que no se pueden deducir fácilmente los desplazamientos y los tamaños de los campos).

Pero AFAIK, no se recomienda el uso de medicamentos genéricos en interoperabilidad.