solo obtener horas hora formato fecha ejemplos convertir cambiar c# datetime marshalling structlayout

c# - obtener - ¿Por qué LayoutKind.Sequential funciona de manera diferente si una estructura contiene un campo DateTime?



formato fecha datetime c# (6)

¿Por qué LayoutKind.Sequential funciona de manera diferente si una estructura contiene un campo DateTime?

Considere el siguiente código (una aplicación de consola que debe compilarse con "no seguro" habilitado):

using System; using System.Runtime.InteropServices; namespace ConsoleApplication3 { static class Program { static void Main() { Inner test = new Inner(); unsafe { Console.WriteLine("Address of struct = " + ((int)&test).ToString("X")); Console.WriteLine("Address of First = " + ((int)&test.First).ToString("X")); Console.WriteLine("Address of NotFirst = " + ((int)&test.NotFirst).ToString("X")); } } } [StructLayout(LayoutKind.Sequential)] public struct Inner { public byte First; public double NotFirst; public DateTime WTF; } }

Ahora, si ejecuto el código anterior, obtengo una salida similar a la siguiente:

Dirección de la estructura = 40F2CC
Dirección de Primera = 40F2D4
Dirección de NotFirst = 40F2CC

Tenga en cuenta que la dirección de First NO es la misma que la dirección de la estructura; sin embargo, la dirección de NotFirst es la misma que la dirección de la estructura.

Ahora comente el campo "DateTime WTF" en la estructura y ejecútelo nuevamente. Esta vez, obtengo una salida similar a esta:

Dirección de la estructura = 15F2E0
Dirección de Primera = 15F2E0
Dirección de NotFirst = 15F2E8

Ahora "Primero" tiene la misma dirección que la estructura.

Encuentro sorprendente este comportamiento dado el uso de LayoutKind.Sequential. ¿Alguien puede dar una explicación? ¿Tiene este comportamiento alguna ramificación al interoperar con estructuras C / C ++ que usan el tipo Com DATETIME?

[EDITAR] NOTA: He verificado que cuando usa Marshal.StructureToPtr () para calcular la estructura, los datos se ordenan en el orden correcto, con el campo "Primero" en primer lugar. Esto parece sugerir que funcionará bien con interoperabilidad. El misterio es por qué cambia el diseño interno, pero, por supuesto, el diseño interno nunca se especifica, por lo que el compilador puede hacer lo que quiera.

[EDIT2] Se eliminó "no seguro" de la declaración de estructura (quedaba restos de algunas pruebas que estaba haciendo).

[EDIT3] La fuente original para esta pregunta era de los foros de MSDN C #:

http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/fb84bf1d-d9b3-4e91-823e-988257504b30


¿Por qué LayoutKind.Sequential funciona de manera diferente si una estructura contiene un campo DateTime?

Está relacionado con el hecho (sorprendente) de que DateTime tiene un diseño "Auto" (enlace a la pregunta SO por mi mismo) . Este código reproduce el comportamiento que viste:

static class Program { static unsafe void Main() { Console.WriteLine("64-bit: {0}", Environment.Is64BitProcess); Console.WriteLine("Layout of OneField: {0}", typeof(OneField).StructLayoutAttribute.Value); Console.WriteLine("Layout of Composite: {0}", typeof(Composite).StructLayoutAttribute.Value); Console.WriteLine("Size of Composite: {0}", sizeof(Composite)); var local = default(Composite); Console.WriteLine("L: {0:X}", (long)(&(local.L))); Console.WriteLine("M: {0:X}", (long)(&(local.M))); Console.WriteLine("N: {0:X}", (long)(&(local.N))); } } [StructLayout(LayoutKind.Auto)] // also try removing this attribute struct OneField { public long X; } struct Composite // has layout Sequential { public byte L; public double M; public OneField N; }

Salida de muestra:

64-bit: True Layout of OneField: Auto Layout of Composite: Sequential Size of Composite: 24 L: 48F050 M: 48F048 N: 48F058

Si eliminamos el atributo de OneField , las cosas se comportan como se espera. Ejemplo:

64-bit: True Layout of OneField: Sequential Layout of Composite: Sequential Size of Composite: 24 L: 48F048 M: 48F050 N: 48F058

Estos ejemplos son con la compilación de la plataforma x64 (por lo que el tamaño 24, tres veces ocho, no es sorprendente), pero también con x86 vemos las mismas direcciones de puntero "desordenadas".

Así que supongo que puedo concluir que el diseño de OneField (resp. DateTime en su ejemplo) tiene influencia en el diseño de la estructura que contiene un miembro de OneField , incluso si esa estructura compuesta en sí misma tiene un diseño Sequential . No estoy seguro de si esto es problemático (o incluso requerido).

Según el comentario de Hans Passant en el otro hilo, ya no hace un intento de mantenerlo secuencial cuando uno de los miembros es una estructura de diseño Auto .


Algunos factores

  • Los dobles son mucho más rápidos si están alineados.
  • Los cachés de la CPU pueden funcionar mejor si no hay "agujeros" en el golpe

Por lo tanto, el compilador de C # tiene algunas reglas no documentadas que usa para tratar de obtener el " mejor " diseño de estructuras, estas reglas pueden tener en cuenta el tamaño total de una estructura, y / o si contiene otra estructura, etc. Si necesita Conozca el diseño de una estructura, entonces debe especificarlo usted mismo en lugar de dejar que el compilador decida.

Sin embargo, LayoutKind.Sequential detiene el compilador cambiando el orden de los campos.


Estás comprobando las direcciones como están dentro de la estructura administrada. Los atributos de Marshal no tienen garantías para la disposición de los campos dentro de las estructuras administradas.

La razón por la que se clasifica correctamente en las estructuras nativas es que los datos se copian en la memoria nativa utilizando los atributos establecidos por los valores de los comisarios.

Por lo tanto, la disposición de la estructura administrada no tiene impacto en la disposición de la estructura nativa. Sólo los atributos afectan la disposición de la estructura nativa.

Si los campos de configuración con atributos de mariscal se almacenaran en datos administrados de la misma manera que los datos nativos, entonces no habría ningún punto en Marshal.StructureToPtr, simplemente copiaría los datos.


Para responder a mis propias preguntas (como se aconseja):

Pregunta: "¿Este comportamiento tiene ramificaciones al interoperar con estructuras C / C ++ que usan el tipo Com DATETIME?"

Respuesta: No, porque el diseño se respeta cuando se utiliza Marshalling. (Verifiqué esto empíricamente).

Pregunta "¿Alguien puede proporcionar una explicación?".

Respuesta: Todavía no estoy seguro de esto, pero como la representación interna de una estructura no está definida, el compilador puede hacer lo que quiera.


Si va a interoperar con C / C ++, siempre sería específico con StructLayout. En lugar de secuencial, iría con Explicit y especificaría cada posición con FieldOffset. Además, añade tu variable Pack.

[StructLayout(LayoutKind.Explicit, Pack=1, CharSet=CharSet.Unicode)] public struct Inner { [FieldOffset(0)] public byte First; [FieldOffset(1)] public double NotFirst; [FieldOffset(9)] public DateTime WTF; }

Suena como que DateTime no puede ser Marshaled de todos modos, solo a una cadena (bingle Marshal DateTime).

La variable Pack es especialmente importante en el código C ++ que puede compilarse en diferentes sistemas que tienen diferentes tamaños de palabra.

También ignoraría las direcciones que se pueden ver cuando se usa un código no seguro. Realmente no importa lo que haga el compilador siempre y cuando el cálculo sea correcto.


Ve a leer las especificaciones de las reglas de diseño con más cuidado. Las reglas de diseño solo rigen el diseño cuando el objeto se expone en una memoria no administrada . Esto significa que el compilador es libre de colocar los campos como quiera hasta que el objeto se exporte realmente. Algo para mi sorpresa, ¡esto es incluso cierto para FixedLayout!

Ian Ringrose tiene razón sobre los problemas de eficiencia del compilador, y eso explica el diseño final que se selecciona aquí, pero no tiene nada que ver con el motivo por el cual el compilador está ignorando las especificaciones de diseño.

Un par de personas han señalado que DateTime tiene un diseño automático. Esa es la fuente principal de su sorpresa, pero la razón es un poco oscura. La documentación para el diseño automático dice que "los objetos definidos con el diseño [Auto] no pueden exponerse fuera del código administrado. Intentar hacerlo genera una excepción". También tenga en cuenta que DateTime es un tipo de valor. Al incorporar un tipo de valor con Diseño automático en su estructura, prometió inadvertidamente que nunca expondría la estructura contenedora al código no administrado (porque al hacerlo expondría el DateTime, y eso generaría una excepción). Dado que las reglas de diseño solo gobiernan los objetos en la memoria no administrada, y su objeto nunca puede ser expuesto a la memoria no administrada, el compilador no está restringido en su elección de diseño y es libre de hacer lo que quiera. En este caso, vuelve a la política de diseño automático para lograr un mejor empaquetamiento y alineación de la estructura.

¡Ahí! ¡No era eso obvio!

Todo esto, por cierto, es reconocible en tiempo de compilación estática. De hecho, el compilador lo está reconociendo para decidir que puede ignorar su directiva de diseño. Habiéndolo reconocido, una advertencia del compilador parece estar en orden. En realidad, no has hecho nada malo, pero es útil que te digan cuando escribes algo que no tiene efecto.

Los diversos comentarios aquí que recomiendan el diseño fijo son generalmente un buen consejo, pero en este caso eso no necesariamente tendría ningún efecto, ya que la inclusión del campo Fecha y hora eximió al compilador de respetar el diseño en absoluto. Peor aún: el compilador no está obligado a honrar el diseño, pero es gratuito para honrar el diseño. Lo que significa que las versiones sucesivas de CLR son libres de comportarse de manera diferente en esto.

El tratamiento del diseño, en mi opinión, es un defecto de diseño en CLI. Cuando el usuario especifica un diseño, el compilador no debe estudiar el entorno. Mejor mantener las cosas simples y hacer que el compilador haga lo que se le dice. Especialmente cuando se trata de diseño. "Inteligente", como todos sabemos, es una palabra de cuatro letras.