Pregunta

Las nuevas extensiones .Net 3.5 permiten la funcionalidad de ser separado de interfaces.

Por ejemplo, en .Net 2.0

public interface IHaveChildren {
    string ParentType { get; }
    int ParentId { get; }

    List<IChild> GetChildren()
}

Puede (en 3.5) se convierten 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 
    }
}

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

La única desventaja es que un resumen de las bases de ejecución puede ser virtual, pero puede que se trabajó en torno a (sería un método de instancia ocultar un método de extensión con el mismo nombre?sería confuso código para hacerlo?)

Otras razones para no utilizar regularmente este patrón?


Aclaración:

Sí, veo que la tendencia con los métodos de extensión es acabar con ellos en todas partes.Me gustaría tener especial cuidado de tener ningún sobre .El valor neto de los tipos sin una gran cantidad de revisión por pares (creo que el único que tenemos en una cadena es un .SplitToDictionary() - similar a la .Split() pero teniendo una clave-valor delimitador demasiado)

Creo que hay toda una mejor práctica del debate ;-)

(Por cierto:DannySmurf, el PM sonidos de miedo.)

Estoy pidiendo específicamente aquí acerca del uso de los métodos de extensión, donde previamente habíamos métodos de interfaz.


Estoy tratando de evitar un montón de niveles de clases base abstractas - las clases de la aplicación de estos modelos en su mayoría ya han clases base.Creo que este modelo podría ser más fácil de mantener y menos demasiado-junto de la incorporación de nuevas jerarquías de objetos.

Esto es lo que MS ha hecho a IEnumerable y IQueryable < t > para Linq?

¿Fue útil?

Solución

Creo que el uso juicioso de los métodos de extensión de poner las interfaces de un más equatable posición con (abstracto) de clases base.


El control de versiones. Una de las ventajas de la base de clases tienen más de interfaces es que usted puede agregar fácilmente nuevos miembros virtuales en una versión posterior, mientras que la adición de miembros a una interfaz de romper los ejecutores construido en contra de la vieja versión de la biblioteca.En su lugar, una nueva versión de la interfaz con los nuevos miembros debe ser creado, y la biblioteca se tiene que evitar o limitar el acceso a los objetos heredados sólo la implementación de la interfaz original.

Como un ejemplo concreto, la primera versión de una biblioteca puede definir una interfaz así:

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

Una vez que la biblioteca ha publicado, no podemos modificar la interfaz sin romper los usuarios actuales.En cambio, en la próxima versión sería necesario definir una nueva interfaz para agregar adicional functionalty:

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

Sin embargo, sólo los usuarios de la nueva biblioteca será capaz de implementar la nueva interfaz.Con el fin de trabajar con código heredado, necesitamos adaptar el viejo de la aplicación de 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 se puede tratar tanto de legado y las implementaciones modernas de forma idéntica.


Sobrecargas. Otra área donde los métodos de extensión puede ser útil en la prestación de sobrecargas para los métodos de interfaz.Usted podría tener un método con varios parámetros para controlar su acción, de los cuales sólo el primero o los dos son importantes en el 90% de los casos.Ya que C# no permite establecer valores predeterminados para los parámetros, los usuarios tienen que llamar a la totalmente parametrizada método cada vez, o cada aplicación debe implementar el trivial sobrecargas del método principal.

En lugar de los métodos de extensión puede ser utilizado para proporcionar el trivial de la sobrecarga de implementaciones:

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 );
  }
  ...
}


Por favor, tenga en cuenta que ambos casos están escritas en términos de las operaciones previstas por las interfaces, y que implican trivial o conocido implementaciones predeterminadas.Dicho esto, sólo puede heredar de una clase una vez, y el uso selectivo de los métodos de extensión puede proporcionar una valiosa manera de lidiar con algunos de los detalles proporcionados por las clases base de que las interfaces de la falta :)


Editar: Un post relacionados por Joe Duffy: Los métodos de extensión como predeterminado de la interfaz de implementaciones de método

Otros consejos

Los métodos de extensión debe de ser utilizados como que:las extensiones.Cualquier crucial de la estructura/diseño relacionados con el código o no trivial de la operación se debe poner en un objeto que está compuesto en/heredado de una clase o interfaz.

Una vez otro objeto que se intenta utilizar la extendida, no van a ver las extensiones y podría tener reimplementar/volver a hacer referencia a ellos de nuevo.

La sabiduría tradicional es que los métodos de Extensión sólo debe ser utilizado para:

  • clases de utilidad, como Vaibhav mencionado
  • ampliación de sellado de la 3ª parte de la Api de

Yo creo que lo mejor que los métodos de extensión reemplazar son todas aquellas clases de utilidad que puedes encontrar en cada proyecto.

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

Mis dos bits.

Veo que separa el dominio de modelo y de interfaz de usuario/vista de la funcionalidad mediante los métodos de extensión como una buena cosa, especialmente ya que puede residir en distintos espacios de nombres.

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 manera los métodos de extensión puede ser usado en forma similar a XAML plantillas de datos.Usted no puede tener acceso privado/miembros protegidos de la clase, pero se permite la abstracción de datos que se mantengan sin excesivo de la duplicación de código en la aplicación.

No hay nada de malo con la extensión de las interfaces, en el hecho de que es como LINQ trabaja para agregar la extensión de los métodos de las clases de colección.

Dicho esto, usted realmente debe hacer esto únicamente en el caso de que usted necesita para proporcionar la misma funcionalidad a través de todas las clases que implementan la interfaz y la funcionalidad no es (y probablemente no debería ser) parte de la "oficial" de la aplicación de cualquiera de las clases derivadas.Con la ampliación de una interfaz también es bueno si sólo es poco práctico para escribir un método de extensión para cada posible tipo derivado que requiere la nueva funcionalidad.

Veo un montón de personas que son partidarias de uso de una clase base para compartir la funcionalidad común.Cuidado con esto - usted debe de favorecer la composición a lo largo de la herencia.La herencia sólo debe ser utilizado para el polimorfismo, cuando tiene sentido a partir de una modelización punto de vista.No es una buena herramienta para la reutilización de código.

En cuanto a la pregunta:Conocer las limitaciones a la hora de hacerlo - por ejemplo, en el código que se muestra, utilizando un método de extensión para implementar GetChildren efectivamente los "sellos" esta aplicación y no permitir que cualquier IHaveChildren impl para proporcionar su propia si es necesario.Si esto es correcto, entonces no me importa la extensión del método de enfoque que mucho.No está escrito en piedra, y por lo general pueden ser refactorizado cuando más la flexibilidad es necesaria más adelante.

Para mayor flexibilidad, utilizando el patrón de estrategia puede ser preferible.Algo así 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();
    }
}

Un poco más.

Si múltiples interfaces tienen el mismo método de extensión de la firma, usted necesita para convertir explícitamente la persona que llama a un tipo de interfaz y, a continuación, llame al método.E. g.

((IFirst)this).AmbigousMethod()

Ouch.Por favor, no extender Interfaces.
Una interfaz es un contrato limpio que una clase debe implementar, y el uso de dichas clases debe se limita a lo que está en el núcleo de la Interfaz para que esto funcione correctamente.

Es por eso que siempre declarar la interfaz como el tipo de cambio real de la clase.

IInterface variable = new ImplementingClass();

A la derecha?

Si usted realmente necesita un contrato con alguna funcionalidad añadida, las clases abstractas son sus amigos.

Rob Connery (Subsónico y MVC en el Escaparate) implementó un IRepository-como patrón en su Tienda de aplicaciones.No es el patrón de arriba, pero sí comparten algunas similitudes.

La capa de datos devuelve IQueryable < t > que permite el consumo de capa para aplicar el filtrado y la ordenación de la expresión en la parte superior de eso.El bono es ser capaz de especificar un único método GetProducts, por ejemplo, y luego decidir adecuadamente en el consumo de la capa de cómo usted quiere que ordenar, filtrar o incluso sólo de un determinado rango de resultados.

No es un enfoque tradicional, pero muy fresco y sin duda un caso de SECO.

Uno de los problemas que veo es que, en una gran empresa, este patrón podría permitir que el código a ser difícil (si no imposible) para que cualquiera pueda entender y utilizar.Si varios desarrolladores están constantemente añadiendo sus propios métodos a las clases existentes, separadas de las clases (y, que Dios nos ayude a todos, a BCL clases, incluso), pude ver a una base de código de girar fuera de control con bastante rapidez.

Incluso en mi propio trabajo, pude ver que esta pasando, con mi PM el deseo de agregar cada uno de los bits de código que trabajamos en la interfaz de usuario o la capa de acceso a datos, pude ver totalmente de él, insistiendo en 20 o 30 de los métodos que están siendo agregado al Sistema.Cadena que sólo tangencialmente relacionados a manejo de cadenas de caracteres.

Necesitaba resolver algo similar:Yo quería tener una Lista de<IIDable> pasa a las extensiones de la función donde IIDable es una interfaz que tiene una larga getId() función.He intentado utilizar GetIds(esta Lista<IIDable> bla), pero el compilador no me permite hacerlo.He usado plantillas y, a continuación, escriba fundido dentro de la función para el tipo de interfaz.Necesitaba esta función para algunos linq to sql generado clases.

Espero que esta ayuda :)

    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");
        }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top