actualización
bien, al principio me dijo "el compilador añade arroja a un bucle foreach
." Esto no es estrictamente exacto: no siempre agregar moldes. Esto es lo que realmente está sucediendo.
En primer lugar, cuando se tiene este foreach
bucle:
foreach (int x in collection)
{
}
... aquí es el esquema básico (en pseudo-C#) de lo que crea el compilador:
int x;
[object] e;
try
{
e = collection.GetEnumerator();
while (e.MoveNext())
{
x = [cast if possible]e.Current;
}
}
finally
{
[dispose of e if necessary]
}
¿Qué? Te oigo decir. ¿Qué quiere decir por [object]
?"
Esto es lo que quiero decir. El bucle foreach
requiere realmente ninguna interfaz, lo que significa que en realidad es un poco mágico. Sólo se requiere que el tipo de objeto que se enumeró expone una GetEnumerator
método, que a su vez debe proporcionar una instancia de algún tipo que proporciona una MoveNext
y una Current
propiedad.
Así que escribió [object]
porque el tipo de e
no necesariamente tiene que ser una implementación de IEnumerator<int>
, o incluso IEnumerator
- que también significa que no necesariamente tiene que implementar IDisposable
(de ahí la parte [dispose if necessary]
).
La parte del código que nos interesa con el propósito de responder a esta pregunta es la parte donde escribí [cast if possible]
. Claramente, dado que el compilador no requiere una implementación real de IEnumerator<T>
o IEnumerator
, no se puede suponer que el tipo de e.Current
sea T
, object
o cualquier cosa intermedia.En cambio, el compilador determina el tipo de e.Current
según el tipo devuelto por GetEnumerator
en tiempo de compilación. Entonces ocurre lo siguiente:
- Si el tipo de es del tipo de la variable local (
x
en el ejemplo anterior), se usa una asignación de recta.
- Si el tipo es convertible al tipo de la variable local (lo cual quiero decir, existe un reparto legal del tipo de
e.Current
al tipo de x
), se inserta un yeso.
- De lo contrario, el compilador generará un error.
Así, en el escenario de enumerar más de un List<int?>
, obtenemos al paso 2 y el compilador ve que la propiedad de tipo List<int?>.Enumerator
Current
es de tipo int?
, que puede convertirse explícitamente a int
.
Así, la línea puede ser compilado por el equivalente a esto:
x = (int)e.Current;
Ahora, lo que hace la mirada explicit
operador como por Nullable<int>
?
De acuerdo con reflector:
public static explicit operator T(T? value)
{
return value.Value;
}
Así es the behavior described by Kent, por lo que yo puedo decir, simplemente una optimización del compilador: el (int)e.Current
conversión explícita se colocarán en línea.
Como respuesta general a su pregunta, me atengo a mi afirmación de que el compilador inserta moldes en un bucle foreach
donde sea necesario.
respuesta original
El compilador inserta automáticamente arroja cuando se requieran en un bucle foreach
por la sencilla razón de que antes de que los genéricos no había IEnumerable<T>
interfaz, solamente IEnumerable
*. La interfaz IEnumerable
expone un IEnumerator
, que a su vez proporciona acceso a una propiedad Current
del tipo object
.
Así que, a menos que el compilador haya realizado el lanzamiento para usted, en el pasado, la única manera en que podría haber usado foreach
hubiera sido con una variable local del tipo object
, que obviamente habría chupado.
* Y, de hecho, foreach
doesn't require any interface at all - sólo el método GetEnumerator
y un tipo de acompañamiento con un MoveNext
y una Current
.
El error me parece normal. Puede convertir desde int? a int, pero no puede convertir un int a bool. –
@Adrian No creo que esta explicación sea correcta. ¿Qué pasa si tengo una cuerda en lugar de bool? Int se puede convertir en cadena, pero el compilador seguiría protestando. – nan
Int no se puede convertir en cadena. No se define conversión implícita o explícita. Si te estás refiriendo al método ToString(), entonces eso es algo diferente. –