unity c# return ienumerable yield-return

c# - unity - ¿Por qué no se puede utilizar "retorno" y "rendimiento de rendimiento" en el mismo método?



yield return list c# (5)

Creo que la razón principal por la que no funciona es porque diseñarlo de una manera que no sea demasiado complicada pero que tenga un buen desempeño al mismo tiempo sería difícil, con un beneficio relativamente pequeño.

¿Qué haría exactamente tu código? ¿Devolvería directamente la matriz, o iteraría sobre ella?

Si devolviera directamente la matriz, entonces tendría que idear reglas complicadas en qué condiciones se permite el return , porque el return después del yield return no tiene sentido. Y probablemente necesitaría generar un código complicado para decidir si el método devolverá el iterador personalizado o la matriz.

Si desea iterar la colección, probablemente desee una mejor palabra clave. Algo así como el yield foreach . Eso fue realmente considerado, pero finalmente no fue implementado. Creo que recuerdo haber leído que la razón principal es que es realmente difícil hacer que funcione bien, si tienes varios iteradores anidados.

¿Por qué no podemos usar tanto el retorno como el rendimiento en el mismo método?

Por ejemplo, podemos tener GetIntegers1 y GetIntegers2 a continuación, pero no GetIntegers3.

public IEnumerable<int> GetIntegers1() { return new[] { 4, 5, 6 }; } public IEnumerable<int> GetIntegers2() { yield return 1; yield return 2; yield return 3; } public IEnumerable<int> GetIntegers3() { if ( someCondition ) { return new[] {4, 5, 6}; // compiler error } else { yield return 1; yield return 2; yield return 3; } }


El compilador reescribe cualquier método con una declaración de yield (retorno o ruptura). Actualmente no puede manejar métodos que pueden o no yield .

Recomiendo leer el capítulo 6 de C # en profundidad de Jon Skeet, del cual el capítulo 6 está disponible de forma gratuita; cubre los bloques de iteradores bastante bien.

No veo ninguna razón por la cual esto no sería posible en futuras versiones del compilador c # sin embargo. Otros lenguajes .Net admiten algo similar en la forma de un operador de "rendimiento desde" (¡ Consulte F# yield! ). Si tal operador existiera en c #, le permitiría escribir su código en la forma:

public IEnumerable<int> GetIntegers() { if ( someCondition ) { yield! return new[] {4, 5, 6}; } else { yield return 1; yield return 2; yield return 3; } }


No, no puede hacer eso: un bloque de iteradores (algo con un yield ) no puede usar el rendimiento normal (sin rendimiento). En su lugar, necesita utilizar 2 métodos:

public IEnumerable<int> GetIntegers3() { if ( someCondition ) { return new[] {4, 5, 6}; // compiler error } else { return GetIntegers3Deferred(); } } private IEnumerable<int> GetIntegers3Deferred() { yield return 1; yield return 2; yield return 3; }

o ya que en este caso específico el código para ambos ya existe en los otros 2 métodos:

public IEnumerable<int> GetIntegers3() { return ( someCondition ) ? GetIntegers1() : GetIntegers2(); }


Teóricamente, creo que no hay ninguna razón por la cual el retorno y el rendimiento no se puedan mezclar: sería una tarea fácil para el compilador transformar sintácticamente primero cualquier return (blabla()); oración en

var myEnumerable = blabla(); foreach (var m in myEnumerable) yield return m; yield break;

y luego continúe (para transformar todo el método en ... ¡lo que sea que lo transforme ahora; ¿una clase interna anónima de IEnumerator ?!)

Entonces, ¿por qué no eligieron implementarlo, aquí hay dos conjeturas:

  • podrían haber decidido que sería confuso para los usuarios tener retorno y rendimiento a la vez,

  • Devolver todo enumerable es más rápido y barato, pero también ansioso; la construcción a través del rendimiento es un poco más costosa (especialmente si se llama de forma recursiva, vea la advertencia de Eric Lippert sobre el recorrido en árboles binarios con declaraciones de rendimiento aquí: https://.com/a/3970171/671084 por ejemplo) pero perezoso. Por lo tanto, un usuario generalmente no querría mezclar esto: si no necesita pereza (es decir, sabe que toda la secuencia) no sufre la penalización de la eficiencia, solo use un método normal. Es posible que hayan querido forzar al usuario a pensar en este sentido.

Por otro lado, parece que hay situaciones en las que el usuario podría beneficiarse de algunas extensiones sintácticas; es posible que desee leer esta pregunta y las respuestas como ejemplo (no es la misma pregunta, pero sí es probable que tenga un motivo similar): ¿ Rendimiento de retorno de muchos?


return es impaciente Devuelve todo el conjunto de resultados a la vez. yield return construye un enumerador. Detrás de las escenas, el compilador de C # emite la clase necesaria para el enumerador cuando se utiliza el yield return . El compilador no busca condiciones de tiempo de ejecución como if ( someCondition ) al determinar si debe emitir el código para un enumerable o tener un método que devuelva una matriz simple. Detecta que en su método está utilizando ambos, lo que no es posible ya que no puede emitir el código para un enumerador y, al mismo tiempo, hacer que el método devuelva una matriz normal y todo esto para el mismo método.