Estoy revisando su enfoque antes de sugerir un enfoque completamente diferente. Prefiero el enfoque diferente, pero parece importante explicar por qué su enfoque tiene fallas.
Elijo implementar IEnumerable<string>
porque hace que su uso sea conveniente
La conveniencia no debe ser mayor que la corrección.
Me pregunto si tu MyFile
clase contendrá más lógica que esta; porque eso afectará la exactitud de esta respuesta. Estoy especialmente interesado en:
.Where(l => ...) //some business logic for filtering
porque si esto es lo suficientemente complejo o dinámico, está ocultando esa lógica en una clase cuyo nombre no revela que filtra su contenido antes de servirlo al consumidor.
Parte de mí espera / asume que esta lógica de filtro está destinada a ser codificada (por ejemplo, un filtro simple que ignora las líneas comentadas, por ejemplo, cómo los archivos .ini consideran que las líneas que comienzan #
como un comentario) y no un conjunto de reglas específico de archivo.
public class MyFile : IEnumerable<string>
Hay algo realmente irritante en tener un singular ( File
) que representa un plural ( IEnumerable
). Un archivo es una entidad singular. Consiste en algo más que su contenido. También contiene metadatos (nombre de archivo, extensión, fecha de creación, fecha de modificación, ...).
Un humano es más que la suma de sus hijos. Un automóvil es más que la suma de sus partes. Una pintura es más que una colección de pinturas y lienzos. Y un archivo es más que una colección de líneas.
Si supongo que su MyFile
clase nunca contendrá más lógica que solo esta enumeración de líneas (y Where
solo aplica un filtro codificado estático simple), entonces lo que tiene aquí es un uso confuso del nombre del "archivo" y su designación prevista. . Esto se puede solucionar fácilmente cambiando el nombre de la clase como FileContent
. Conserva la sintaxis deseada:
foreach(var line in new FileContent(@"C:\Folder\File.txt"))
También tiene más sentido desde un punto de vista semántico. El contenido de un archivo puede dividirse en líneas separadas. Esto todavía supone que el contenido del archivo es texto y no binario, pero eso es lo suficientemente justo.
Sin embargo, si su MyFile
clase contendrá más lógica, la situación cambia. Hay algunas maneras en que esto podría suceder:
- Comienza a usar esta clase para representar los metadatos del archivo, no solo su contenido.
Cuando comienza a hacer esto, el archivo representa el archivo en el directorio , que es más que solo su contenido.
El enfoque correcto aquí es entonces lo que has hecho MyFile2
.
- El
Where()
filtro comienza a tener una lógica de filtro complicada que no está codificada, por ejemplo, cuando diferentes archivos comienzan a filtrarse de manera diferente.
Cuando comienza a hacer esto, los archivos comienzan a tener identidades propias, ya que tienen su propio filtro personalizado. Esto significa que tu clase se ha convertido más en un FileType
que en un FileContent
. Los dos comportamientos necesitan ser ya sea separados o combinados usando la composición (lo que favorece su MyFile2
enfoque), o preferentemente ambos (clases separadas para el FileType
y FileContent
comportamiento, y luego tener ambos compuestos en la MyFile
clase).
Una sugerencia totalmente diferente.
Tal como están las cosas, tanto tu MyFile
como MyFile2
existen para darte una envoltura alrededor de tu .Where(l => ...)
filtro. En segundo lugar, está creando efectivamente una clase para envolver un método estático ( File.ReadLines()
), que no es un gran enfoque.
Por otro lado, no entiendo por qué elegiste hacer tu clase sealed
. En todo caso, esperaría que la herencia sea su principal característica: diferentes clases derivadas con diferente lógica de filtrado (suponiendo que sea más complejo que un simple cambio de valor, porque la herencia no debe usarse solo para cambiar un solo valor)
Reescribiría toda su clase como:
foreach(var line in File.ReadLines(...).Where(l => ...))
El único inconveniente de este enfoque simplificado es que tendría que repetir el Where()
filtro cada vez que desee acceder al contenido del archivo. Estoy de acuerdo en que eso no es deseable.
Sin embargo, parece excesivo que cuando quieras crear un reutilizable Where(l => ...)
declaración , también obligue a esa clase a implementar File.ReadLines(...)
. Estás agrupando más de lo que realmente necesitas.
En lugar de intentar ajustar el método estático en una clase personalizada, creo que es mucho más apropiado si lo ajusta en un método estático propio:
public static IEnumerable<string> GetFilteredFileContent(string filePath)
{
return File.ReadLines(filePath).Where(l => ...);
}
Suponiendo que tiene filtros diferentes, puede pasar el filtro apropiado como un parámetro. Le mostraré un ejemplo que puede manejar múltiples filtros, que debería poder manejar todo lo que necesita hacer mientras maximiza la reutilización:
public static class MyFile
{
public static Func<string, bool> IgnoreComments =
(l => !l.StartsWith("#"));
public static Func<string, bool> OnlyTakeComments =
(l => l.StartsWith("#"));
public static Func<string, bool> IgnoreLinesWithTheLetterE =
(l => !l.ToLower().contains("e"));
public static Func<string, bool> OnlyTakeLinesWithTheLetterE =
(l => l.ToLower().contains("e"));
public static IEnumerable<string> ReadLines(string filePath, params Func<string, bool>[] filters)
{
var lines = File.ReadLines(filePath).Where(l => ...);
foreach(var filter in filters)
lines = lines.Where(filter);
return lines;
}
}
Y su uso:
MyFile.ReadLines("path", MyFile.IgnoreComments, MyFile.OnlyTakeLinesWithTheLetterE);
Este es solo un ejemplo de ejecución que pretende demostrar que los métodos estáticos tienen más sentido que crear una clase aquí.
No se deje atrapar por los detalles de la implementación de los filtros. Puede implementarlos como desee (personalmente me gusta la parametrización Func<>
debido a su extensibilidad y adaptabilidad inherentes a la refactorización). Pero dado que en realidad no fue un ejemplo de los filtros que tiene la intención de usar, hice una suposición para mostrarle un ejemplo viable.
ver new X().Y()
parece que algo salió mal con el diseño de la clase)
En su enfoque, podría haber hecho new X().Y
que sea menos irritante.
Sin embargo, creo que su disgusto new X().Y()
demuestra el punto de que siente que una clase no está justificada aquí, pero sí un método; que solo se puede representar sin una clase siendo estático.
new
para este caso de uso, dificultad en las pruebas unitarias, ambigua cuando yo / O pueden ocurrir excepciones, etc. Lo siento, no estoy tratando de ser grosero. Creo que me he acostumbrado demasiado a los beneficios de la inyección de dependencia .IniFileContent
.