with uso only may compiling code appear c# unsafe

c# - uso - ¿Por qué los tampones de tamaño fijo solo pueden ser de tipos primitivos?



uso de virtual en c# (3)

¿Qué es un buffer fijo?

Desde MSDN:

En C #, puede usar la declaración fija para crear un búfer con una matriz de tamaño fijo en una estructura de datos. Esto es útil cuando está trabajando con código existente, como código escrito en otros idiomas, DLL preexistentes o proyectos COM. La matriz fija puede tomar cualquier atributo o modificador permitido para los miembros de estructura normales. La única restricción es que el tipo de matriz debe ser bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float o double .

Voy a citar al Sr. Hans Passant en cuanto a por qué un búfer fijo DEBE ser unsafe . Podría ver ¿Por qué un búfer de tamaño fijo (matrices) debe ser inseguro? para más información.

Porque un "buffer fijo" no es una matriz real. Es un tipo de valor personalizado, casi la única forma de generar uno en el lenguaje C # que conozco. No hay forma de que el CLR verifique que la indexación de la matriz se realiza de manera segura. El código tampoco es verificable. La demostración más gráfica de esto:

using System; class Program { static unsafe void Main(string[] args) { var buf = new Buffer72(); Console.WriteLine(buf.bs[8]); Console.ReadLine(); } } public struct Buffer72 { public unsafe fixed byte bs[7]; }

Puede acceder arbitrariamente al marco de pila en este ejemplo. La técnica estándar de inyección de desbordamiento de búfer estaría disponible para el código malicioso para parchear la dirección de retorno de la función y forzar su código para saltar a una ubicación arbitraria.

Sí, eso es bastante inseguro.

¿Por qué un búfer fijo no puede contener tipos de datos no primitivos?

Simon White planteó un punto válido:

Voy a ir con "complejidades añadidas al compilador". El compilador tendría que verificar que no se aplicó ninguna funcionalidad específica de .NET a la estructura que se aplicó a los elementos enumerables. Por ejemplo, genéricos, implementación de interfaces, propiedades aún más profundas de arreglos no primitivos, etc. Sin duda, el tiempo de ejecución también tendrá algunos problemas de interoperabilidad con ese tipo de cosas.

Y Ibasa:

"Pero eso ya lo hace el compilador". Sólo en parte. El compilador puede hacer las comprobaciones para ver si un tipo está administrado pero eso no se encarga de generar código para leer / escribir estructuras en búferes fijos. Se puede hacer (no hay nada que lo detenga a nivel CIL) simplemente no se implementa en C #.

Por último, Mehrdad:

Creo que es literalmente porque no quieren que uses buffers de tamaño fijo (porque quieren que uses código administrado). El hecho de que sea demasiado fácil interoperar con el código nativo hace que sea menos probable que use .NET para todo, y quieren promover el código administrado tanto como sea posible.

La respuesta parece ser un rotundo "simplemente no está implementado".

¿Por qué no está implementado?

Mi conjetura es que el costo y el tiempo de implementación simplemente no valen la pena para ellos. Los desarrolladores preferirían promover el código administrado sobre el código no administrado. Posiblemente podría hacerse en una versión futura de C #, pero el CLR actual carece de la complejidad necesaria.

Una alternativa podría ser el tema de la seguridad. Dado que los búferes fijos son inmensamente vulnerables a todo tipo de problemas y riesgos de seguridad si se implementan pobremente en su código, puedo ver por qué el uso de ellos se desaconseja sobre el código administrado en C #. ¿Por qué poner mucho trabajo en algo de lo que te gustaría desalentar el uso?

Tenemos que interoperar mucho con el código nativo, y en este caso es mucho más rápido usar estructuras inseguras que no requieren cálculo de referencias. Sin embargo, no podemos hacer esto cuando las estructuras contienen buffers de tamaño fijo de tipos no primitivos. ¿Por qué es un requisito del compilador de C # que los buffers de tamaño fijo sean solo de los tipos primitivos? ¿Por qué no se puede hacer un búfer de tamaño fijo de una estructura como:

[StructLayout(LayoutKind.Sequential)] struct SomeType { int Number1; int Number2; }


Entiendo su punto de vista ... por otro lado, supongo que podría ser algún tipo de compatibilidad avanzada reservada por Microsoft. Su código está compilado para MSIL y es un sistema de .NET Framework y OS específico para diseñarlo en la memoria.

Puedo imaginar que puede venir una nueva CPU de Intel que requerirá diseñar variables a cada 8 bytes para obtener el rendimiento óptimo. En ese caso, será necesario en el futuro, en algún futuro .NET Framework 6 y algún futuro Windows 9 para diseñar estas estructuras de manera diferente. En este caso, su código de ejemplo sería una presión para que Microsoft no cambie el diseño de la memoria en el futuro y no acelere el .NET framework al HW moderno.

Es solo especulación ...

¿Intentaste configurar FieldOffset? Ver la unión de C ++ en C #


Los buffers de tamaño fijo en C # se implementan con una función CLI llamada "clases opacas". La sección I.12.1.6.3 de Ecma-335 describe:

Algunos idiomas proporcionan estructuras de datos de múltiples bytes cuyos contenidos se manipulan directamente mediante operaciones aritméticas y de direccionamiento indirecto de direcciones. Para admitir esta función, la CLI permite crear tipos de valor con un tamaño específico pero sin información sobre sus miembros de datos. Las instancias de estas "clases opacas" se manejan exactamente de la misma manera que las instancias de cualquier otra clase, pero las instrucciones ldfld, stfld, ldflda, ldsfld y stsfld no deben usarse para acceder a sus contenidos.

El "no información sobre sus miembros de datos" y "ldfld / stfld no se utilizarán" son el problema. La 2ª regla pone el kibosh en las estructuras, necesita ldfld y stfld para acceder a sus miembros. El compilador de C # no puede proporcionar una alternativa, el diseño de una estructura es un detalle de implementación en tiempo de ejecución. También la razón por la que no puede usar el operador sizeof en una estructura. Decimal y Nullable <> están fuera porque también son estructuras. IntPtr está fuera porque su tamaño depende del bitness del proceso, lo que dificulta que el compilador de C # genere la dirección del código de operación ldind / stind utilizado para acceder al búfer. Las referencias de los tipos de referencia están fuera porque el GC debe poder encontrarlas de nuevo y no puede hacerlo según la primera regla. Los tipos de enumeración tienen un tamaño variable que depende de su tipo base; Suena como un problema solucionable, no del todo seguro de por qué lo omitieron.

Lo que acaba de dejar los mencionados por la especificación del lenguaje C #: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double o bool. Sólo los tipos simples con un tamaño bien definido.