una puede patron observer llamar interfaz interfaces instancia implementar implementacion ejemplo diseño declaracion declara cómo crear caracteristicas c# .net-3.5 extension-methods

c# - patron - no se puede crear una instancia de una interfaz



Patrones de interfaz de extensión (11)

Las nuevas extensiones en .Net 3.5 permiten que la funcionalidad se divida desde las interfaces.

Por ejemplo, en .Net 2.0

public interface IHaveChildren { string ParentType { get; } int ParentId { get; } List<IChild> GetChildren() }

Puede (en 3.5) convertirse en:

public interface IHaveChildren { string ParentType { get; } int ParentId { get; } } public static class HaveChildrenExtension { public static List<IChild> GetChildren( this IHaveChildren ) { //logic to get children by parent type and id //shared for all classes implementing IHaveChildren } }

Esto me parece que es un mejor mecanismo para muchas interfaces. Ya no necesitan una base abstracta para compartir este código, y funcionalmente el código funciona de la misma manera. Esto podría hacer que el código sea más fácil de mantener y probar.

La única desventaja es que una implementación de bases abstractas puede ser virtual, pero se puede solucionar (¿podría un método de instancia ocultar un método de extensión con el mismo nombre? ¿Sería confuso el código para hacerlo?)

¿Alguna otra razón para no usar regularmente este patrón?

Aclaración:

Sí, veo que la tendencia con los métodos de extensión es terminar con ellos en todas partes. Sería particularmente cuidadoso al tener cualquiera en los tipos de valores .Net sin una gran cantidad de revisión por pares (creo que el único que tenemos en cadena es un .SplitToDictionary() - similar a .Split() pero tomando un delimitador de valores-clave también)

Creo que hay todo un debate sobre mejores prácticas allí ;-)

(Por cierto: DannySmurf, tu PM suena aterrador).

Específicamente pregunto aquí sobre el uso de métodos de extensión donde anteriormente teníamos métodos de interfaz.

Estoy tratando de evitar muchos niveles de clases base abstractas: las clases que implementan estos modelos en su mayoría ya tienen clases base. Creo que este modelo podría ser más fácil de mantener y estar menos acoplado que agregar jerarquías de objetos adicionales.

¿Esto es lo que MS ha hecho a IEnumerable y IQueryable para Linq?


Creo que lo mejor que los métodos de extensión reemplazan son todas las clases de utilidad que encuentras en cada proyecto.

Al menos por ahora, creo que cualquier otro uso de los métodos de extensión causaría confusión en el lugar de trabajo.

Mis dos bits.


Un problema que puedo ver es que, en una empresa grande, este patrón podría permitir que el código sea difícil (si no imposible) de entender y usar. Si múltiples desarrolladores constantemente agregan sus propios métodos a las clases existentes, se separan de esas clases (y, Dios nos ayude a todos, incluso a las clases BCL), pude ver una base de código que giraba fuera de control bastante rápido.

Incluso en mi propio trabajo, pude ver que esto sucedía, con el deseo de mi PM de agregar cada bit de código en el que trabajamos tanto a la interfaz de usuario como a la capa de acceso a datos, pude verlo insistir en que se agregaran 20 o 30 métodos a System.String que solo está relacionado tangencialmente con el manejo de cadenas.


Ay. Por favor, no extienda las interfaces.
Una interfaz es un contrato limpio que una clase debe implementar, y su uso de dichas clases debe restringirse a lo que está en la interfaz principal para que esto funcione correctamente.

Es por eso que siempre declaras la interfaz como el tipo en lugar de la clase real.

IInterface variable = new ImplementingClass();

¿Derecha?

Si realmente necesita un contrato con alguna funcionalidad adicional, las clases abstractas son sus amigos.


Los métodos de extensión se deben usar simplemente como: extensiones. Cualquier código crucial relacionado con la estructura / diseño o la operación no trivial debe colocarse en un objeto compuesto / heredado de una clase o interfaz.

Una vez que otro objeto intente utilizar el extendido, no verá las extensiones y podría tener que volver a aplicarlas o volver a referenciarlas.

La sabiduría tradicional es que los métodos de extensión solo deberían usarse para:

  • clases de utilidad, como mencionó Vaibhav
  • extendiendo las API de terceros selladas

No hay nada de malo en extender interfaces, de hecho, así es como LINQ trabaja para agregar los métodos de extensión a las clases de colección.

Dicho esto, solo debería hacer esto en caso de que necesite proporcionar la misma funcionalidad en todas las clases que implementan esa interfaz y esa funcionalidad no es (y probablemente no debería ser) parte de la implementación "oficial" de cualquier derivado clases La extensión de una interfaz también es buena si no es práctico escribir un método de extensión para cada posible tipo derivado que requiera la nueva funcionalidad.


Veo que separar el dominio / modelo y la funcionalidad UI / view usando métodos de extensión es algo bueno, especialmente porque pueden residir en espacios de nombres separados.

Por ejemplo:

namespace Model { class Person { public string Title { get; set; } public string FirstName { get; set; } public string Surname { get; set; } } } namespace View { static class PersonExtensions { public static string FullName(this Model.Person p) { return p.Title + " " + p.FirstName + " " + p.Surname; } public static string FormalName(this Model.Person p) { return p.Title + " " + p.FirstName[0] + ". " + p.Surname; } } }

De esta forma, los métodos de extensión se pueden usar de manera similar a las plantillas de datos XAML. No puede acceder a los miembros privados / protegidos de la clase, pero permite que la abstracción de datos se mantenga sin duplicación excesiva de códigos en toda la aplicación.


Veo a mucha gente abogando por usar una clase base para compartir una funcionalidad común. Tenga cuidado con esto: debe favorecer la composición por sobre la herencia. La herencia solo debe usarse para el polimorfismo, cuando tiene sentido desde un punto de vista de modelado. No es una buena herramienta para la reutilización de código.

En cuanto a la pregunta: tenga en cuenta las limitaciones al hacer esto; por ejemplo, en el código que se muestra, usar un método de extensión para implementar GetChildren efectivamente ''sella'' esta implementación y no permite que IHaveChildren impl lo proporcione si es necesario. Si esto está bien, entonces no me importa mucho el método de extensión. No está escrito en piedra, y por lo general se puede refactorizar fácilmente cuando más flexibilidad se necesita más adelante.

Para una mayor flexibilidad, puede ser preferible usar el patrón de estrategia. Algo como:

public interface IHaveChildren { string ParentType { get; } int ParentId { get; } } public interface IChildIterator { IEnumerable<IChild> GetChildren(); } public void DefaultChildIterator : IChildIterator { private readonly IHaveChildren _parent; public DefaultChildIterator(IHaveChildren parent) { _parent = parent; } public IEnumerable<IChild> GetChildren() { // default child iterator impl } } public class Node : IHaveChildren, IChildIterator { // *snip* public IEnumerable<IChild> GetChildren() { return new DefaultChildIterator(this).GetChildren(); } }


Rob Connery (Subsonic y MVC Storefront) implementó un patrón parecido a un repositorio IR en su aplicación Storefront. No es exactamente el patrón anterior, pero comparte algunas similitudes.

La capa de datos devuelve IQueryable que permite a la capa consumidora aplicar expresiones de filtrado y ordenamiento además de eso. El bono es poder especificar un solo método de GetProducts, por ejemplo, y luego decidir de manera adecuada en la capa de consumo cómo desea esa clasificación, filtrado o incluso solo un rango particular de resultados.

No es un enfoque tradicional, pero es genial y definitivamente es un caso de SECO.


Necesitaba resolver algo similar: quería que se pasara List <IIDable> a la función de extensiones donde IIDable es una interfaz que tiene una función getId () larga. Intenté usar GetIds (esta Lista <IIDable> bla) pero el compilador no me permitió hacerlo. Usé plantillas en su lugar y luego escribí convertidas dentro de la función al tipo de interfaz. Necesitaba esta función para algunas clases generadas de sqq a sql.

Espero que esto ayude :)

public static List<long> GetIds<T>(this List<T> original){ List<long> ret = new List<long>(); if (original == null) return ret; try { foreach (T t in original) { IIDable idable = (IIDable)t; ret.Add(idable.getId()); } return ret; } catch (Exception) { throw new Exception("Class calling this extension must implement IIDable interface"); }


Un poco más.

Si múltiples interfaces tienen la misma firma de método de extensión, necesitarás convertir explícitamente al llamante a un tipo de interfaz y luego llamar al método. P.ej

((IFirst)this).AmbigousMethod()


Creo que el uso juicioso de los métodos de extensión coloca a las interfaces en una posición más equitativa con las clases base (abstractas).


Versiones Una de las ventajas que tienen las clases base sobre las interfaces es que puede agregar fácilmente nuevos miembros virtuales en una versión posterior, mientras que agregar miembros a una interfaz romperá los implementadores creados con la versión anterior de la biblioteca. En su lugar, se debe crear una nueva versión de la interfaz con los nuevos miembros, y la biblioteca tendrá que trabajar o limitar el acceso a los objetos heredados que solo implementan la interfaz original.

Como ejemplo concreto, la primera versión de una biblioteca podría definir una interfaz como esta:

public interface INode { INode Root { get; } List<INode> GetChildren( ); }

Una vez que la biblioteca ha sido liberada, no podemos modificar la interfaz sin interrumpir a los usuarios actuales. En cambio, en la próxima versión necesitaríamos definir una nueva interfaz para agregar funcionalidades adicionales:

public interface IChildNode : INode { INode Parent { get; } }

Sin embargo, solo los usuarios de la nueva biblioteca podrán implementar la nueva interfaz. Para trabajar con el código heredado, debemos adaptar la implementación anterior, que un método de extensión puede manejar muy bien:

public static class NodeExtensions { public INode GetParent( this INode node ) { // If the node implements the new interface, call it directly. var childNode = node as IChildNode; if( !object.ReferenceEquals( childNode, null ) ) return childNode.Parent; // Otherwise, fall back on a default implementation. return FindParent( node, node.Root ); } }

Ahora todos los usuarios de la nueva biblioteca pueden tratar tanto las implementaciones heredadas como las modernas de forma idéntica.


Sobrecarga Otra área donde los métodos de extensión pueden ser útiles es proporcionar sobrecargas para los métodos de interfaz. Es posible que tenga un método con varios parámetros para controlar su acción, de los cuales solo el primero o dos son importantes en el caso del 90%. Como C # no permite establecer valores predeterminados para los parámetros, los usuarios tienen que llamar al método completamente parametrizado cada vez, o cada implementación debe implementar las sobrecargas triviales para el método central.

En cambio, los métodos de extensión se pueden usar para proporcionar las implementaciones de sobrecarga triviales:

public interface ILongMethod { public bool LongMethod( string s, double d, int i, object o, ... ); } ... public static LongMethodExtensions { public bool LongMethod( this ILongMethod lm, string s, double d ) { lm.LongMethod( s, d, 0, null ); } ... }


Tenga en cuenta que estos dos casos están escritos en términos de las operaciones proporcionadas por las interfaces e implican implementaciones predeterminadas triviales o bien conocidas. Dicho esto, solo puede heredar de una clase una vez, y el uso específico de los métodos de extensión puede proporcionar una forma valiosa de lidiar con algunas de las sutilezas proporcionadas por las clases base que las interfaces carecen :)

Editar: una publicación relacionada por Joe Duffy: métodos de extensión como implementaciones de métodos de interfaz por defecto