Question

Les nouvelles extensions en .Net 3.5 permettre de fonctionnalités pour être séparé des interfaces.

Par exemple, dans .Net 2.0

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

    List<IChild> GetChildren()
}

Peut (3.5) deviennent:

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 
    }
}

Cela me semble être un meilleur mécanisme pour de nombreuses interfaces.Ils n'ont plus besoin d'un résumé de la base de partager ce code, et fonctionnellement le code fonctionne de la même façon.Cela pourrait rendre le code plus maintenable et plus facile à tester.

Le seul inconvénient étant qu'un résumé des bases de la mise en œuvre peut être virtuel, mais qui peut être contourné (serait une méthode d'instance masquer une extension de la méthode avec le même nom?serait-il du code source de confusion pour le faire?)

Toutes les autres raisons de ne pas utiliser régulièrement ce schéma?


Précisions:

Ouais, je vois la tendance avec les méthodes d'extension est de se retrouver avec eux partout.Je serais particulièrement prudent d'avoir tout de suite .La valeur nette types, sans beaucoup d'examen par les pairs (je pense que la seule que nous ayons sur une chaîne de caractères est un .SplitToDictionary() - semblable à .Split() mais en prenant une valeur-clé de délimiteur de trop)

Je pense qu'il y a un ensemble de meilleures pratiques de débat ;-)

(Soit dit en passant:DannySmurf, votre PM sons effrayants.)

Je suis venu demander ici sur l'utilisation de méthodes d'extension où, auparavant, nous avions des méthodes d'interface.


J'essaie d'éviter beaucoup de niveaux de classes de base abstraites - les classes de mise en œuvre de ces modèles pour la plupart ont déjà des classes de base.Je pense que ce modèle pourrait être plus facile à gérer et moins trop couplée que l'ajout d'autres hiérarchies d'objets.

Est-ce que MS a fait de IEnumerable et IQueryable pour Linq?

Était-ce utile?

La solution

Je pense que l'utilisation judicieuse des méthodes d'extension mis interfaces sur une plus equatable position avec (résumé) les classes de base.


La gestion des versions. Un avantage des classes de base ont sur les interfaces, c'est que vous pouvez facilement ajouter de nouveaux membres virtuels dans une version ultérieure, tandis que l'ajout de membres à une interface se casser les responsables de l'implémentation construit contre l'ancienne version de la bibliothèque.Au lieu de cela, une nouvelle version de l'interface avec les nouveaux membres doit être créé, et la bibliothèque aura pour contourner ou de limiter l'accès à l'héritage des objets que la mise en œuvre de l'interface d'origine.

À titre d'exemple concret, la première version d'une bibliothèque peut définir une interface comme ceci:

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

Une fois la bibliothèque a publié, nous ne pouvons pas modifier l'interface sans casser les utilisateurs actuels.Au lieu de cela, dans la prochaine version, nous aurions besoin de définir une nouvelle interface pour ajouter des functionalty:

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

Toutefois, seuls les utilisateurs de la nouvelle bibliothèque sera en mesure de mettre en œuvre la nouvelle interface.Afin de travailler avec le code existant, nous devons adapter la vieille mise en œuvre, qui d'une méthode d'extension peut traiter gentiment:

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

Maintenant, tous les utilisateurs de la nouvelle bibliothèque peuvent traiter à la fois l'héritage et les implémentations modernes à l'identique.


Les surcharges. Un autre domaine où les méthodes d'extension peut être utile en donnant des surcharges de méthodes d'interface.Vous pouvez avoir une méthode avec plusieurs paramètres de contrôle de son action, dont seule la première ou les deux sont importants dans 90% des cas.Depuis C# ne permet pas de définition de valeurs par défaut pour les paramètres, les utilisateurs doivent appeler le paramétrée méthode à chaque fois, ou à chaque mise en œuvre doit mettre en œuvre le trivial surcharges de la méthode noyau d'.

Au lieu de cela les méthodes d'extension peut être utilisée pour fournir le trivial surcharge implémentations:

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


Veuillez noter que deux de ces cas sont écrits en termes d'opérations prévues par les interfaces, et d'impliquer trivial ou le bien-connu des implémentations par défaut.Cela dit, vous ne pouvez hériter d'une classe une fois, et l'utilisation ciblée des méthodes d'extension peut être un moyen précieux pour faire face à certaines subtilités fournis par les classes de base que les interfaces manque :)


Edit: Un poste par Joe Duffy: Les méthodes d'Extension comme interface par défaut de la méthode des implémentations

Autres conseils

Les méthodes d'Extension doivent être utilisés que:extensions.Toute structure cruciale/liés à la conception du code ou de la non-trivial opération doit être placée dans un objet qui est composé en/a hérité d'une classe ou d'une interface.

Une fois qu'un autre objet essaie d'utiliser l'étendue, ils ne verront pas les extensions et pourrait avoir à ré-écrire/re-référencer à nouveau.

La sagesse traditionnelle est que les méthodes d'Extension doit être utilisée uniquement pour:

  • les classes utilitaires, comme mentionné Vaibhav
  • l'extension de scellés 3ème partie Api

Je pense que la meilleure chose que des méthodes d'extension de remplacer ce sont tous ceux de l'utilitaire de classes que vous trouverez dans chaque projet.

Au moins pour l'instant, j'ai l'impression que toute autre utilisation de méthodes d'Extension serait une source de confusion dans le milieu de travail.

Mes deux bits.

Je vois séparant le domaine/modèle de l'INTERFACE et/fonctionnalités d'affichage à l'aide de méthodes d'extension comme une bonne chose, surtout depuis qu'ils peuvent résider dans des espaces de noms distincts.

Par exemple:

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 cette façon, les méthodes d'extension peut être utilisé de la même façon à XAML des modèles de données.Vous ne pouvez pas accès privé/protégé les membres de la catégorie, mais il permet à l'abstraction de données pour être maintenu sans trop de duplication de code dans l'application.

Il n'y a rien de mal avec l'extension interfaces, en fait, c'est comment LINQ œuvres d'ajouter l'extension des méthodes pour les classes de collection.

Cela étant dit, vous devriez vraiment le faire uniquement dans le cas où vous avez besoin de fournir les mêmes fonctionnalités dans toutes les classes qui implémentent cette interface, et que la fonctionnalité n'est pas (et ne devraient probablement pas être) une partie de la "officielle" de la mise en œuvre de toutes les classes dérivées.L'extension de l'interface est également bon, si c'est juste impossible à écrire une extension de la méthode pour chaque type dérivé qui exige la nouvelle fonctionnalité.

Je vois beaucoup de gens défendre à l'aide d'une classe de base communs de la fonctionnalité.Soyez prudent avec cette - vous devriez préférer la composition au cours de l'héritage.L'héritage doit être utilisé uniquement pour le polymorphisme, lorsque cela a du sens à partir d'une modélisation de point de vue.Il n'est pas un bon outil pour la réutilisation du code.

Comme pour la question:Être ware des limites lorsque le faire - par exemple dans le code montré, à l'aide d'une extension de la méthode à mettre en œuvre GetChildren efficacement "scellés" cette application et ne permet pas tout IHaveChildren impl pour fournir son propre si nécessaire.Si c'est OK, alors je n'ai pas l'esprit de la méthode d'extension en approche beaucoup.Il n'est pas gravé dans la pierre, et peuvent généralement être facilement remaniée lors de la plus grande souplesse est nécessaire plus tard.

Pour une plus grande flexibilité, en utilisant le modèle de stratégie peut être préférable.Quelque chose comme:

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 petit peu plus.

Si plusieurs interfaces ont la même extension signature de la méthode, vous avez besoin de convertir explicitement l'appelant à un type d'interface, puis appelez la méthode.E. g.

((IFirst)this).AmbigousMethod()

Ouch.Merci de ne pas prolonger les Interfaces.
Une interface est un contrat propre qu'une classe doit implémenter, et de votre utilisation de ces catégories doit être limitée à ce qui est dans le cœur de l'Interface pour que cela fonctionne correctement.

C'est pourquoi vous devez toujours déclarer l'interface comme type, au lieu de la classe réelle.

IInterface variable = new ImplementingClass();

Droit?

Si vous avez vraiment besoin d'un contrat avec quelques fonctionnalités supplémentaires, les classes abstraites sont vos amis.

Rob Connery (Subsonique et MVC Vitrine) a mis en place IRepository-comme le modèle dans sa Vitrine de l'application.Ce n'est pas tout à fait le modèle ci-dessus, mais il ne partage quelques similitudes.

La couche de données renvoie IQueryable qui permet de consommer de la couche à appliquer le filtrage et le tri expression sur le dessus de cela.Le bonus est d'être capable de spécifier une seule méthode GetProducts, par exemple, et ensuite de décider de manière appropriée dans la consommation de la couche de la façon dont vous voulez que le tri, le filtrage, ou même juste un particulier de la gamme de résultats.

Pas une approche traditionnelle, mais très cool et certainement un cas de sécheresse.

Un problème que je peux voir, c'est que, dans une grande entreprise, ce modèle pourrait permettre à du code de devenir difficile (voire impossible) pour toute personne à comprendre et à utiliser.Si plusieurs développeurs sont constamment en ajoutant leurs propres méthodes de classes existantes, distincts de ceux des classes (et, que Dieu nous aide tous, à la BCL classes de même), j'ai pu voir une base de code hors de contrôle plutôt rapidement.

Même à mon propre travail, je pouvais voir ce qui se passe, avec mon PM de la volonté d'ajouter tous les bits de code que nous travaillons à l'INTERFACE utilisateur ou de la couche d'accès aux données, je pouvais tout voir en insistant sur 20 ou 30 méthodes d'être ajouté au Système.Chaîne de caractères qui ne sont tangentiellement liés à la manipulation des chaînes.

J'ai besoin de résoudre quelque chose de similaire:Je voulais avoir une Liste<IIDable> passée à les extensions de fonction où IIDable est une interface qui a un long getId() fonction.J'ai essayé d'utiliser GetIds(cette Liste<IIDable> bla), mais le compilateur ne me permet pas de le faire.J'ai utilisé les modèles de la place, et tapez ensuite coulé à l'intérieur de la fonction pour le type d'interface.J'ai besoin de cette fonction pour certains linq to sql classes générées.

J'espère que cela aide :)

    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");
        }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top