[Debate] Nombre de Namespaces en métodos extensores ¿Y tu que opinas?
Ayer, a raíz de un post del colega Javier Torrecilla sobre métodos extensores, unos cuantos de nosotros entre los que estaban el propio Javier y Jorge Serrano nos enzarzamos en una discusión en twitter acerca del mejor modo de declarar nuestros métodos extensores.
Pongamos un ejemplo: Supongamos que queremos crear un método extensor para comprobar si un valor está entre dos valores (el clásico between de toda la vida).
Agrego una clase llamada ExtensionMethods a mi proyecto, o a otro proyecto mi solución y agrego este código:
namespace CustomExtensions
{
public static class ExtensionMethods
{
public static bool Between<T>(this T @value, T min, T max) where T : IComparable<T>
{
return @value.CompareTo(min) >= 0 && @value.CompareTo(max) <= 0;
}
}
}
Suponiendo que estamos en un proyecto de tipo WinForms, si queremos utilizar este método extensor sobre un valor de tipo int basta con ir a cualquier formulario y llamar al método between sobre un valor de este tipo. Por ejemplo:
Oops! Que pasa? Por que no aparece el método extensor? Bueno, como ya os habréis dado cuenta el método extensor está declarado dentro de un namespce llamado ‘CustomExtensions’, que es distinto al namespace del formulario en el que lo estoy probando, con lo que no podemos usarlo directamente si previamente no hacemos un using:
using CustomExtensions;
Vale, ahora si que aparece:
Bien. Esto en si no es nada del otro mundo, pero la cuestión es que si deseamos evitar declarar el using (tenéis que pensar que este método extensor lo podéis reutilizar en 1000 proyectos distintos), no tenemos otra opción que:
- Declarar el método extensor en un namespace que se llame igual que el namespace en el que se va a usar.
- Declarar el método extensor en un namespace que se llame igual que el namespace del tipo que estamos extendiendo.
- Pasar de todo y llamarlo al namespace como queramos, y que a la hora de usarlo debamos usar un using para agregarlo.
Particularmente, al extender tipos básicos (string, int) soy partidario del segundo punto, de modo que si vamos a extender elementos de tipo IComparable, en lugar de usar el namespace ‘CustomExtensions’ prefiero usar el nombre del namespace que contiene la definición de este tipo, o sea ‘System’:
namespace System
{
public static class ExtensionMethods
{
public static bool Between<T>(this T @value, T min, T max) where T : IComparable<T>
{
return @value.CompareTo(min) >= 0 && @value.CompareTo(max) <= 0;
}
}
}
Pero ese es mi punto de vista, tu que opinas? Twitteros manifestaos! 🙂
19 Responsesso far
Buenas!
Pues depende…
De hecho la opción (1) presupone que se sabe donde y cuando se va a usar el método extensor… y si solo se va a usar en un sólo sitio quizá me haría un método helper clásico (privado y static en la misma clase), ya que de ese modo queda más claro que el método se usa sólo aquí. 😉
En el caso que dices (between), yo también soy partidario del segundo punto, porque es un rollo tener que recordad en que namespace exacto se encuentra un método de extensión (y eso suponiendo que sepas que existe este método de expansión, que si no…). Y ese es un método que se supone que está en un framework que extiende un tipo de .NET y de forma genérica, por lo que es lógico que esté declarado en el mismo namespace que el tipo al que extiende.
Pero no siempre. P.ej. en ASP.NET MVC es muy normal hacerte métodos de expansión sobre el HtmlHelper, para que renderice distintos temas.
A veces son renderizaciones MUY específicas que sólo se usan en UNA vista (o un par a lo sumo) de un controlador. En este caso prefiero tener los métodos de extensión en namespaces distintos (y no en System.Web.Mvc que es donde reside el HtmlHelper) y usar @using en la vista para importar este namespace. De este modo evito que en las otras vistas intellisense me muestre ese método extensor (que no quiero).
Un saludo maestro! 😉
Grande @Lluis,
Bueno lo primero gracias por la mención :p
Personalmente creo, que queda más bonito declarado en el mismo Namespace. Cuando cre el post, y vi el primer comentario la verdad es que no me daba cuenta a lo que se referia el usuario, cuando me decía usar el mismo Namespace. Después de reflexionar con la almohada, me di cuenta de que es una posibilidad mas y bien pensado, es lo mejor, proque si te olvidas de hacer el using y otro quiere intentar usar el método a simple vista no va a poder.
Como ya dije ayer da gusto ver estos debates!
Bueno, yo soy de la opinión contraria. Creo que así se distingue mejor los métodos que declaramos nosotros respecto a los que vienen incluidos en el Framework.
Perder el Intelisense «No mola»… prefiero meter el namespace System pero q las extensiones «muy nuestras» tengan prefijo.
Yo suelo usar ROP_ en clases similares o confundibles para indicar que «son mías» y no del sistema.
Por alusiones contesto… 🙂
Que ganas tenía de que Lluis escribiera esta entrada y de que opináramos entre todos… veo diversidad, que siempre enriquece y es bueno… ¡mola!
Bueno, a lo que voy.
Entiendo que poner el namespace System (por poner uno) está muy bien porque nos olvidamos del using y vamos más directos.
Por otro lado, yo me pongo en la tesitura de quien tiene un Framework interno dentro de su empresa (caso que es el mío concretamente) y que es a lo mejor a lo que se refiere Carles también con su comentario.
En este caso y siguiendo lo que yo hago (que a lo mejor alguien me hace ver que no es lo mejor y me corrige), tendría mis namespaces del tipo: Empresa.Framework.Extensions, etc.
Bien, en este caso me pongo a codificar y me doy cuenta de que esa «maravillosa» extensión no la tengo y tengo que poner el using.
En este caso, mato dos pájaros de un tiro. Por un lado, debo poner el using, por otro, sé que ese using extiende en «algo».
Cierto es, que pierdo la rapidez de simplemente tirar millas sin poner el using… pero total… tengo que poner en la referencia sí o sí el ensamblado de extensibilidad… así que qué más me da.
Ahora voy a ponerme en el otro lugar.
Hago mi namespace extendido y aunque tenga más namespaces llamados Empresa.Framework.XXX, Empresa.Framework.YYY, Empresa.Framework.ZZZ, ahora tengo uno llamado System.
Bien… no pasa nada… es mío y lo llamo como quiero.
Referencio en el proyecto mi ensamblado y a tirar millas, sin using ni nada.
Ahora bien, en el segundo proyecto, pierdo la noción directa de si estoy usando mi ensamblado de extensibilidad o no.
En segundo lugar, rompo con la filosofía que perseguía mi Framework, y ahora tengo cuatro ensamblados, tres de ellos que se llaman Empresa.Framework… y un cuarto que es System.
Para el gusto los colores, lo sé, pero entendiendo lo que argumenta Lluis, no lo comparto al menos para siempre.
En el caso del proyecto en el que estoy actualmente implicado, no lo tengo nada claro.
Igual para otro tipo de proyecto sí, pero en mi caso actual, tengo una librería con métodos extensores y pertenece al propio Framework.
Luego, desde el propio Framework utilizo en diferentes sitios esta librería, pero tengo claro en todo momento que la estoy usando.
Es decir, tengo todo el código estructurado, claramente identificado, organizado y usando el «using» sé que estoy haciendo uso de los métodos extensores.
Cuando abordé esto en el proyecto, me planteé hacerlo como namespace del Framework interno que estábamos haciendo o no, y al final tomé esta decisión por lo que comento en la línea anterior.
No veo muchas más diferencias, pero a mí me parece mejor organizado tal y como lo he hecho hasta ahora.
Por cierto, hacer lo que comenta @RamonEEZA a mí me parece un refrito. Si a él le sirve para organizarse genial, pero es quedarse en medio de las dos posturas planteadas, pero para eso, prefiero hacerlo como lo hago yo.
Si alguien me hace cambiar de opinión, encantado, que siempre es bueno aprender. 🙂
Muy buena entrada @Lluis. 😉
@Jorge en un namespace nuevo (System.Linq). En este caso tiene sentido porque «todo el conjunto de extensiones» conforman una pequeña libreria (linq).
Si tienes muchas extensiones, a veces puede ser bueno tenerlas en su propio namespace. Especialmente si dichas extensiones «están relacionadas entre sí». P.ej. es lo que ha hecho Microsoft con Linq: agrupar extensiones sobre IEnumerable
En el fondo, cada caso es particular: es importante como nos organizamos nosotros, quien más va a usar ese código (caso de frameworks que comentabais).
Un saludo! 😉
¡Buenas chicos!
Me he perdido vuestra conversación de Twitter por la mañana pero voy a intentar engancharme ahora 🙂
Por la experiencia que he tenido con los extension methods, pienso que podemos dividirlos en dos grupos principalmente:
1. Aquellos que consideramos esenciales dentro del Framework y que «no estorban» como métodos que consideramos que faltan en la clase String by default, en el DateTime (como recuperar la semana del año GRGR :P), etcétera que podrían encajar perfectamente dentro del mismo Namespace que la clase que se extiende.
y 2. Aquello que están restringidos a la lógica de negocio. En este caso es posible que se usen en otros proyectos dentro de la misma empresa o posiblemente no pero la extensión me sugiere más un caso particular que general como podría ser el caso 1.
No sé si me explico pero pienso que los métodos generales deberían llevar el mismo Namespace y los particulares distintos.
Buena entrada Lluis 🙂
Hola buenas,
Personalmente me gusta tener las dependencias lo menos escondidas posible, con lo que me gusta más la opción de utilizar nuestro propio namespace.
Saludos!
Yo lo que hago es considerar los metodos extensores como cualquier otro helper, por tanto, nunca los incluyo en el namespace del Framework, a pesar de que pierda comodidad.
Joer, me voy un momento al baño y la que habéis liado (es broma) 😛
A ver, creo que al fin y al cabo todo se resume a un problema de organización: Cuando creamos un método extensor estamos ampliando la funcionalidad de un objeto -como su propio nombre indica- y además como dice el inefable Hadi (al menos creo que era él quién lo dijo) lo estamos haciendo sin violar la ‘O’ de SOLID (ya sabéis, lo del principio Open/Close).
Pero que es exactamente lo que estamos extendiendo? Un tipo de datos simple o complejo? Es decir, en los comentarios de @Edu y @Gisela hay un detalle básico: Podemos dividirlos en:
1-Todo aquello que *creo* que debería estar en el FWK y que ¡por tutatis! no lo está. Ergo, lo extiendo…
2-Aquella funcionalidad específica que es propietaria de mi negocio / producto, y que *creo* que debe estar en su propio namespace.
Creo que en el primer caso SI es apropiado usar el namespace System, mientras que en el segundo me inclino por lo que propone @Jorge.
¿Usar sólo un método para todo? pues como queráis, para gustos los colores, pero yo seguiré usando este método 😛
Muchas gracias a todos por el debate! Me encantan estos hilos 🙂
Enga ¿Quién da mas?
A mi personalmente me da mal rollo utilizar el namespace del System, pero sobre todo porque venga algun desarrollador después de mi y no lo termine de entender. Pero viendo el caso que pone Lluis, creo que queda bastante elegante y más entendible porque realmente estás extendiendo el framework. Depende siempre del caso que estés desarrollando. 🙂
Estoy con Gisela, hay 2 clasificaciones para estos métodos los «que extienden» el framework base y el framework de empresa con la lógica de negocio.
Si vas a dejarlo en System considero imprescindible poner siempre un prefijo como decía RamonEEZA, ya que lo que hoy le falta al Framework, mañana podría estar implementado y si has atinado bien, quizá hasta con el mismo nombre, así que un prefijo se hace indispensable.
Yo soy de la opinión que tienen que ir en su propio namespace. Por varios motivos:
– Mi código va en mis namespaces.
– System no es mío. Como han dicho antes lo mismo ese método aparece en el framework más adelante.
– Si lo que hago es una librería de terceros, obligo al que la usa a comerse el método sin poder hacer nada al respecto. Si depende de un using ya es su decisión usar las extensiones o no.
– Y más grave: si todo el mundo hiciera esto no sería raro que al usar dos librerías de terceros las dos definieran el mismo método, y ya la tenemos liada.
Un saludo,
Vicente
O sea que el String.GetRandomString() que uso en mi proyecto heredado desde hace 2 años, no es parte de .Net Fwk??? Plop !!!
A ver, yo lo simplifico con «organizate de la mejor forma que puedas». Es decir, si uno se acostumbra a trabajar con String.GetRandomString() y piensas que es parte de .Net, pues mal lo llevas en no conocerlo o el día que descubras que no existe, pues deberás conocer los métodos extensores, putear al que lo creo y no te aviso y llevartelo a donde quieras.
En resumen -> es todo una cuestión de sentido comun 😀
Salu2
PD: por suerte, los productos bien hechos como el modelo de objetos de servidor de TFS2010, no necesitan extensiones ya que cubren TODAS las funcionalidades y casuísticas existentes … de ahi el comentario 😀
Aquí está mi respuesta mucho más completa al respecto: http://blog.svpino.com/2011/01/namespaces-y-extensiones-es-system-el.html
Dejo como preguna al aire: porque las extensiones de Microsoft para IEnumerable están en System.Linq y no en System.Collections.Generic?
Si queremos actuar de forma correcta, es decir, hacerlo en un namespace Propio, quizás se podría hacer lo siguiente:
Compilar todas nuestras extensiones en en una DLL, y modificar la plantilla básica de proyecto en VS, para que automáticamente se haga la referencia a dicha DLL y el using.
Quizás se demasiada vuelta de tuerca… XD
Vicente, las clases, interfaces y extensiones que se encuentran en System.Linq están todas relacionadas, y a pesar que funcionan con IEnumerable, tienen un objetivo muy diferente al de las clases en System.Collections.Generic.
En System.Collections.Generic encontramos interfaces y clases que permiten definir colecciones genéricas, sin embargo, los elementos dentro de System.Linq no están enfocados solamente a las colecciones genéricas, sino que van mucho más allá.
Linq no es más que un conjunto de facilidades para búsqueda de propósito general que aplica para cualquier fuente de información, ya sea XML, SQL, o coleciones de objetos. Es por esto que tiene sentido agrupar todas estas clases/interfaces/extensiones en su propio namespace.