c# - recorrer - ¿Por qué foreach no encuentra mi método de extensión GetEnumerator?
list.foreach c# (6)
Algunos offtopic: si quieres hacerlo más legible escribe.
foreach ( DataRow r in tbl.Rows ) yield return r;
como
foreach (DataRow row in tbl.Rows)
{
yield return row;
}
ahora a tu problema ... prueba esto
public static IEnumerable<T> GetEnumerator<T>(this DataTable table)
{
return table.Rows.Cast<T>();
}
Estoy tratando de hacer un código más legible. Por ejemplo, foreach(var row in table) {...}
lugar de foreach(DataRow row in table.Rows) {...}
.
Para hacer esto creé un método de extensión:
namespace System.Data {
public static class MyExtensions {
public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) {
foreach ( DataRow r in tbl.Rows ) yield return r;
}
}
}
Pero el compilador aún arroja la foreach statement cannot operate on variables of type ''System.Data.DataTable'' because ''System.Data.DataTable'' does not contain a public definition for ''GetEnumerator''
.
Para confirmar que implementé el método de extensión de forma adecuada, probé el siguiente código y el compilador no tuvo ningún problema con él.
for ( IEnumerator<DataRow> enm = data.GetEnumerator(); enm.MoveNext(); ) {
var row = enm.Current;
...
}
Antes de decir que se debe a que IEnumerator
o IEnumerator<DataRow>
no está implementado, considere que se compila lo siguiente:
public class test {
public void testMethod() {
foreach ( var i in new MyList( 1, ''a'', this ) ) { }
}
}
public class MyList {
private object[] _list;
public MyList( params object[] list ) { _list = list; }
public IEnumerator<object> GetEnumerator() { foreach ( var o in _list ) yield return o; }
}
Dentro de una sentencia foreach, el compilador está buscando un método de instancia de GetEnumerator. Por lo tanto, el tipo (aquí DataTable) debe implementar IEnumerable. Nunca encontrará su método de extensión, ya que es estático. Tienes que escribir el nombre de tu método de extensión en el foreach.
namespace System.Data {
public static class MyExtensions {
public static IEnumerable<DataRow> GetEnumerator( this DataTable table ) {
foreach ( DataRow r in table.Rows ) yield return r;
}
}
}
foreach(DataRow row in table.GetEnumerator())
.....
Para evitar confusiones, sugeriría usar un nombre diferente para su método de extensión. Tal vez algo como GetRows ()
El método GetEnumerator en su clase de prueba no es estático, el método de extensión es. Esto tampoco compila:
class test
{
}
static class x
{
public static IEnumerator<object> GetEnumerator(this test t) { return null; }
}
class Program
{
static void Main(string[] args)
{
foreach (var i in new test()) { }
}
}
Para que el azúcar de sintaxis de foreach funcione, su clase debe exponer un método de instancia GetEnumerator público.
Hay mucha confusión en las otras respuestas hasta ahora. (Aunque la respuesta de Preston Guillot es bastante buena, en realidad no pone un dedo en lo que está pasando aquí). Déjame aclarar.
En primer lugar, simplemente estás fuera de suerte. C # requiere que la colección utilizada en una instrucción foreach:
- Implementar un
GetEnumerator
público que coincida con el patrón requerido. - Implementar
IEnumerable
(y por supuesto,IEnumerable<T>
requiereIEnumerable
) - Sea dinámico, en cuyo caso simplemente pateamos la lata por el camino y hacemos el análisis en tiempo de ejecución.
El resultado es que el tipo de colección debe implementar el GetEnumerator
una manera u otra. Proporcionar un método de extensión no lo corta.
Esto es desafortunado. En mi opinión, cuando el equipo de C # agregó métodos de extensión a C # 3, deberían haber modificado las características existentes, como foreach
(y quizás incluso using
) para considerar los métodos de extensión. Sin embargo, el calendario fue extremadamente ajustado durante el ciclo de publicación de C # 3 y es probable que se eliminen los elementos de trabajo adicionales que no lograron la implementación de LINQ a tiempo. No recuerdo exactamente lo que dijo el equipo de diseño sobre este punto y ya no tengo mis notas.
Esta situación desafortunada es el resultado del hecho de que los idiomas crecen y evolucionan; las versiones antiguas están diseñadas para las necesidades de su tiempo, y las nuevas versiones tienen que construir sobre esa base. Si, en sentido contrario, C # 1.0 hubiera tenido métodos de extensión y genéricos, entonces el bucle foreach
podría haber sido diseñado como LINQ: como una simple transformación sintáctica. Pero no fue así, y ahora estamos atrapados con el legado del diseño pre-genérico y de pre-extensión.
En segundo lugar , parece haber cierta desinformación en otras respuestas y comentarios sobre lo que se requiere precisamente para que cada uno trabaje. No es necesario que implemente IEnumerable
. Para obtener más detalles sobre esta característica comúnmente mal entendida, vea mi artículo sobre el tema .
En tercer lugar , parece haber alguna duda sobre si este comportamiento está realmente justificado por la especificación. Es. La especificación no indica explícitamente que los métodos de extensión no se consideran en este caso, lo cual es desafortunado. Sin embargo, la especificación es extremadamente clara sobre lo que sucede:
El compilador comienza haciendo una búsqueda de miembros para GetEnumerator
. El algoritmo de búsqueda de miembros se documenta en detalle en la sección 7.3, y la búsqueda de miembros no considera los métodos de extensión , solo los miembros reales . Los métodos de extensión solo se consideran después de que la resolución de sobrecarga regular haya fallado , y aún no hemos llegado a la resolución de sobrecarga. (Y sí, los métodos de extensión son considerados por el acceso de miembros , pero el acceso de miembros y la búsqueda de miembros son operaciones diferentes).
Si la búsqueda de miembros no encuentra un grupo de métodos , el intento de coincidir con el patrón falla. Por lo tanto, el compilador nunca pasa a la parte de resolución de sobrecarga del algoritmo y, por lo tanto, nunca tiene la oportunidad de considerar métodos de extensión.
Por lo tanto, el comportamiento que usted describe es consistente con el comportamiento especificado.
Le aconsejo que lea la sección 8.8.4 de la especificación con mucho cuidado si desea comprender con precisión cómo un compilador analiza una instrucción foreach
.
Cuarto , lo aliento a que dedique su tiempo a agregar valor a su programa de alguna otra manera. El convincente beneficio de
foreach (var row in table)
terminado
foreach(var row in table.Rows)
Es diminuto para el desarrollador e invisible para el cliente. Pase su tiempo agregando nuevas funciones, corrigiendo errores o analizando el rendimiento, en lugar de hacer que el código ya sea perfectamente claro sea cinco caracteres más corto.
La colección de objetos en un foreach
debe implementar System.Collections.IEnumerable
o System.Collections.Generic.IEnumerable<T>
.
Si tiene un fuerte deseo de habilitar esto, entonces podría crear una clase contenedora que implemente IEnumerable
y tenga un puntero para usted DataTable
. Alternativamente, podría heredar DataTable
en una nueva clase e implementar IEnumerable
.
La legibilidad del código es más a menudo la preferencia personal. Personalmente, encuentro que sus cambios en la declaración de foreach
menos legibles (pero estoy seguro de que hay muchos de los que están de acuerdo con usted).
Su extensión es equivalente a:
public static IEnumerable<TDataRow> GetEnumerator<TDataRow>( this DataTable tbl ) {
foreach ( TDataRow r in tbl.Rows ) yield return r;
}
GetEnumerator<TDataRow>
no es el mismo método que GetEnumerator
Esto funcionará mejor:
public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) {
foreach (DataRow r in tbl.Rows ) yield return r;
}