2009-02-27 10 views
1

J'ai une situation où je mappe des classes qui ont une hiérarchie d'héritage. J'ai une liste d'articles dont chaque classe sait qu'elle a besoin. Par exemple:Quelle est une façon élégante d'agréger les propriétés des sous-classes en C#?

public class PersonMapper 
{ 
    private string[] attributes = new[] {"firstName", "lastName"}; 
} 

public class UserMapper : PersonMapper 
{ 
    private string[] attributes = new[] {"username"}; 
} 

public class EmployeeMapper : PersonMapper 
{ 
    private string[] attributes = new[] {"employeeId"}; 
} 

Je voudrais faire un appel de méthode qui retourne l'ensemble de tous les super-classes comme ceci:

EmployeeMapper mapper = new EmployeeMapper(); 
string[] attributes = mapper.GetAttributes(); 
Assert.That(attributes, Has.Member("firstname")); 

Je peux penser à des solutions, mais ils semblent trop compliqué . J'ai l'impression qu'il y a une solution élégante à ce problème qui me manque. Quelqu'un peut-il aider?

Merci d'avance!

Répondre

3

Vous pouvez avoir une méthode/propriété virtuelle sur la superclasse appelée GetAttributes qui retourne le tableau privé. Ensuite, dans chaque sous-classe, remplacez-le comme ceci. Commencez par appeler la méthode GetAttribute de base pour obtenir le tableau de classes de base, et combinez-la avec le tableau de sous-classes, et renvoyez le nouveau tableau.

Exemple de code

:

public class PersonMapper 
    { 
     private string[] attributes = new[] { "firstName", "lastName" }; 
     public virtual string[] GetAttributes() 
     { 
      return attributes; 
     } 
    } 

public class EmployeeMapper : PersonMapper 
    { 
     private string[] attributes = new[] { "employeeId" }; 
     public override string[] GetAttributes() 
     { 
      return base.GetAttributes().Union(this.attributes).ToArray(); 
     } 
    } 
+0

acceptés dans les 4 minutes. Grands garçons de discussion, merci! –

+0

D'où vient l'Union()? Est-il disponible parce que System.Array implémente IEnumerable? –

+0

Oui, c'est l'une des méthodes d'extension Linq sur IEnumerable . – BFree

0

Sans réflexion, il n'y a aucun moyen de le faire dans l'état actuel. Ce que vous pouvez faire est de créer une méthode virtuelle protégée qui permet à chaque classe d'ajouter ses attributs à une liste et de l'agréger de cette façon.

public class PersonMapper { 
    protected virtual void AggregateAttributes(List<string> list) { 
    list.AddRange(attributes); 
    } 
    public List<string> GetAttributes() { 
    List<string> list = new List<string>(); 
    AggregateAttributes(list); 
    return list; 
    } 
} 

public class UserMapper { 
    protected override void AggergateAttributes(List<string> list) { 
    base.AggregateAttributes(list); 
    list.AddRange(attributes); 
    } 
} 

public class EmployeeMapper { 
    protected override void AggergateAttributes(List<string> list) { 
    base.AggregateAttributes(list); 
    list.AddRange(attributes); 
    } 
} 
1

Je serais tenté de développer un attribut personnalisé qui obtient appliqué à chacune des propriétés que je me soucie de suivre et utiliser la réflexion pour énumérer les propriétés avec cet attribut appliqué. Vous pouvez utiliser le chargement paresseux pour que la liste d'attributs ne soit remplie qu'une seule fois. Je ferais aussi probablement GetAttributes() une méthode statique (classe).

public class MappedAttribute : Attribute 
{ 
} 

public class Person 
{ 
    [Mapped] 
    public string FirstName { get; set; } 

    [Mapped] 
    public string LastName { get; set; } 

    public string DisplayName 
    { 
     get { return FirstName + " " + LastName; } 
    } 
} 

public static class Mapper 
{ 
    public static IList<string> Attributes(Type t) 
    { 
      List<string> attributes = new List<string>(); 

      foreach (PropertyInfo pInfo in t.GetProperties()) 
      { 
       MappedAttribute attr = pInfo.GetCustomAttributes(typeof(MappedAttribute),false) 
              .Cast<MappedAttribute>() 
              .FirstOrDefault(); 
       if (attr != null) 
       { 
        attributes.Add(pInfo.Name); 
       } 
      } 

      return attributes; 
    } 
} 
0

Je pencherais pour une approche plus simple:

class Entity { 
    public virtual IEnumerable<string> Attributes { get { yield return "Name"; } } 
} 

class Person : Entity { 
    public override IEnumerable<string> Attributes { 
     get { 
      return new string[] { "FirstName", "LastName" } 
       .Concat(base.Attributes); 
     } 
    } 
} 

class User : Person { 
    public override IEnumerable<string> Attributes { 
     get { 
      return new string[] { "UserName" } 
       .Concat(base.Attributes); 
     } 
    } 
} 

(post-scriptum Ces types de hiérarchies travaillent rarement dans les langues sur un héritage.)

0
public class PersonMapper 
{ 
    protected List<string> attributes = 
     new List<string>() {"firstName", "lastName"}; 
    public string[] GetAttributes() 
    { 
     //defensive copy 
     return attributes.ToArray(); 
    } 
} 

public class EmployeeMapper : PersonMapper 
{ 
    public EmployeeMapper() 
    { 
     attributes.Add("employeeId"); 
    } 
} 
Questions connexes