Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

225
Vistas
¿Por qué una stackalloc de longitud cero hace que el compilador de C# esté feliz de permitir stackallocs condicionales?

La siguiente "solución" es muy confusa para mí; el escenario aquí es decidir condicionalmente si usar la pila o un búfer arrendado según el tamaño; sin embargo, es una optimización bastante específica pero a veces necesaria: con la implementación "obvia" (número 3, aplazar la asignación definitiva hasta que realmente queramos asignar it), el compilador se queja con CS8353:

Un resultado de una expresión stackalloc de tipo 'Span<int>' no se puede usar en este contexto porque puede estar expuesto fuera del método que lo contiene.

La reproducción corta (sigue una reproducción completa) es:

 // take your pick of: // Span<int> s = stackalloc[0]; // works // Span<int> s = default; // fails // Span<int> s; // fails if (condition) { // CS8353 happens here s = stackalloc int[size]; } else { s = // some other expression } // use s here

Lo único que puedo pensar aquí es que el compilador realmente está marcando que stackalloc está escapando del contexto en el que ocurre stackalloc , y está agitando una bandera para decir "No puedo probar si esto será seguro más adelante en el método", pero al tener el stackalloc[0] al principio, estamos empujando el alcance del contexto "peligroso" más alto, y ahora el compilador está feliz de que nunca escape del alcance "peligroso" (es decir, en realidad nunca deja el método , ya que estamos declarando en el alcance superior). ¿Es correcto este entendimiento, y es solo una limitación del compilador en términos de lo que se puede probar?

Lo que es realmente interesante (para mí) es que = stackalloc[0] es fundamentalmente un no-op de todos modos , lo que significa que al menos en la forma compilada el número de trabajo 1 = stackalloc[0] es idéntico al número fallido 2 = default .

Reproducción completa (también disponible en SharpLab para ver la IL ).

 using System; using System.Buffers; public static class C { public static void StackAllocFun(int count) { // #1 this is legal, just initializes s as a default span Span<int> s = stackalloc int[0]; // #2 this is illegal: error CS8353: A result of a stackalloc expression // of type 'Span<int>' cannot be used in this context because it may // be exposed outside of the containing method // Span<int> s = default; // #3 as is this (also illegal, identical error) // Span<int> s; int[] oversized = null; try { if (count < 32) { // CS8353 happens at this stackalloc s = stackalloc int[count]; } else { oversized = ArrayPool<int>.Shared.Rent(count); s = new Span<int>(oversized, 0, count); } Populate(s); DoSomethingWith(s); } finally { if (oversized is not null) { ArrayPool<int>.Shared.Return(oversized); } } } private static void Populate(Span<int> s) => throw new NotImplementedException(); // whatever private static void DoSomethingWith(ReadOnlySpan<int> s) => throw new NotImplementedException(); // whatever // note: ShowNoOpX and ShowNoOpY compile identically just: // ldloca.s 0, initobj Span<int>, ldloc.0 static void ShowNoOpX() { Span<int> s = stackalloc int[0]; DoSomethingWith(s); } static void ShowNoOpY() { Span<int> s = default; DoSomethingWith(s); } }
over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

No es una respuesta a "por qué"; sin embargo, podría cambiarlo a un operador ternario que divida el resultado de la asignación de la matriz en un Span:

 public static void StackAllocFun(int count) { int[] oversized = null; try { Span<int> s = ((uint)count < 32) ? stackalloc int[count] : (oversized = ArrayPool<int>.Shared.Rent(count)).AsSpan(0, count); Populate(s); DoSomethingWith(s); } finally { if (oversized is not null) { ArrayPool<int>.Shared.Return(oversized); } } }
over 4 years ago · Santiago Trujillo Denunciar

0

La función Span<T> / ref es esencialmente una serie de reglas sobre a qué ámbito puede escapar un valor dado por valor o por referencia. Si bien esto está escrito en términos de alcances de métodos, es útil simplificar solo una de dos declaraciones:

  1. El valor no se puede devolver desde el método.
  2. El valor puede ser devuelto desde el método.

El documento de seguridad de intervalo entra en gran detalle sobre cómo se calcula el alcance para varias declaraciones y expresiones. Sin embargo, la parte relevante aquí es cómo se procesan los locales .

La conclusión principal es que si un local puede o no regresar se calcula en el momento de la declaración local. En el momento en que se declara el local, el compilador examina el inicializador y toma una decisión sobre si el método puede o no devolver el local. En el caso de que haya un inicializador, el local podrá regresar si la expresión de inicialización puede devolverse.

¿Cómo maneja el caso en el que se declara un local pero no hay un inicializador? El compilador tiene que tomar una decisión: ¿puede o no puede volver? Al diseñar la característica, tomamos la decisión de que el valor predeterminado sería "se puede devolver" porque es la decisión que causó la menor cantidad de fricción para los patrones existentes.

Eso nos dejó con el problema de cómo los desarrolladores podían declarar un local que no era seguro devolver pero que también carecía de un inicializador. Eventualmente nos decidimos por el patrón de = stackalloc [0] . Esta es una expresión que es segura para optimizar y un fuerte indicador, básicamente un requisito, de que el local no es seguro para regresar.

Sabiendo que esto explica el comportamiento que está viendo:

  • Span<int> s = stackalloc[0] : esto no es seguro para regresar, por lo tanto, el último stackalloc tiene éxito
  • Span<int> s = default : es seguro devolverlo porque default es seguro devolverlo. Esto significa que el stackalloc posterior falla porque está asignando un valor que no es seguro para regresar a un local que está marcado como seguro para regresar.
  • Span<int> s; : es seguro devolverlo porque es el valor predeterminado para locales no inicializados. Esto significa que el stackalloc posterior falla porque está asignando un valor que no es seguro para regresar a un local que está marcado como seguro para regresar.

La verdadera desventaja del = stackalloc[0] es que solo es aplicable a Span<T> . No es una solución general para ref struct . En la práctica, aunque no es un gran problema para otros tipos. Hay algunas especulaciones sobre cómo podríamos hacerlo más general , pero no hay suficiente evidencia para justificar hacerlo en este momento.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda