2010-11-27 34 views
3

un coup d'oeil à ce code s'il vous plaît:Property Get d'un objet générique en C#

public void BindElements<T>(IEnumerable<T> dataObjects) 
{ 
    Paragraph para = new Paragraph(); 

    foreach (T item in dataObjects) 
    { 
     InlineUIContainer uiContainer = 
      this.CreateElementContainer(item.FirstName ?????)    
     para.Inlines.Add(uiContainer); 
    }       

    FlowDocument flowDoc = new FlowDocument(para); 
    this.Document = flowDoc; 
} 

Lorsqu'en écriture dans Visual Studio « item.XXX » Je devrais obtenir les propriétés de mon entitiy comme .FirstName ou. Nom de famille. Je ne sais pas si dataObjects est un IEnumerable ou IOrder etc ... il doit être générique!

Comment puis-je obtenir l'élément de formulaire Propriétés réelles? Seulement avec réflexion?

+0

Pourquoi ** doit-il être générique? – Oded

+0

Je pensais que c'était mieux que BindElements (objet dataObjects) public parce que je dois faire des castings ... – Elisabeth

Répondre

6

Oded is right, il ne semble pas (pour lui ou moi) avoir un quelconque sens pour essayer de rendre cette méthode générique. Vous essayez de généraliser une méthode dont la fonctionnalité est réellement spécifique à quelques types.

Maintenant, cela dit, il semble que le vrac de la fonction est indépendant de cette propriété que vous souhaitez accéder. Alors pourquoi ne pas le diviser en deux parties: celle qui peut être genericized, et ce qui ne peut pas:

Quelque chose comme ceci:

void BindElements<T, TProperty>(IEnumerable<T> dataObjects, 
           Func<T, TProperty> selector) 
{ 
    Paragraph para = new Paragraph(); 

    foreach (T item in dataObjects) 
    { 
     // Notice: by delegating the only type-specific aspect of this method 
     // (the property) to (fittingly enough) a delegate, we are able to 
     // package MOST of the code in a reusable form. 
     var property = selector(item); 

     InlineUIContainer uiContainer = this.CreateElementContainer(property) 
     para.Inlines.Add(uiContainer); 
    } 

    FlowDocument flowDoc = new FlowDocument(para); 
    this.Document = flowDoc; 
} 

Ensuite, vos types traitant de surcharges spécifiques, par exemple, IPerson, peut réutiliser ce code (que je soupçonne peut-être ce que vous étiez après tout le long de la réutilisation de code):

public void BindPeople(IEnumerable<IPerson> people) 
{ 
    BindElements(people, p => p.FirstName); 
} 

... puis pour IOrder:

public void BindOrders(IEnumerable<IOrder> orders) 
{ 
    BindElements(orders, o => p.OrderNumber); 
} 

... et ainsi de suite.

+0

+1 - Exactement l'approche à laquelle je pensais. – Oded

+0

oui oui Dan Tao Je suis totalement après la réutilisation du code! – Elisabeth

+0

ah Je pensais avoir déjà commenté ça ok je le fais maintenant ...: @Dan Tao => Et si je voulais passer DEUX de ces "sélecteurs" comme Prénom et Nom, comment le paramètre de la méthode BindElements changerait-il? Je n'ai jamais semé/lu de ce sélecteur TProperty + et je ne le comprends pas vraiment ;-) Je commence juste à faire des choses génériques vraiment génériques. – Elisabeth

4

Si vous ajoutez un constraint au type générique (dire qu'il doit implémenter l'interface IPerson), vous pouvez utiliser toutes les méthodes définies sur l'interface:

public void BindElements<T>(IEnumerable<T> dataObjects) where T : IPerson 

Si IPerson définit FirstName et LastName peroperties, vous pouvez les utiliser avec T.

Voir le lien pour les différents types de generic constraints possible.

+0

Battez-moi. Ma réponse aurait été presque exactement la même. – siride

+0

@Oded désolé pour confondre cela ne devrait pas signifier ... est un IEnumerable cela devrait signifier ICustomer ou IOrder etc ... mais vous l'avez déjà eu presque ... Le problème est que je ne peux pas mettre IPerson là parce que cela détruirait le générique fonction de cette méthode! L'utilisateur de UserControl qui expose la méthode BindElements publique passe une liste , ou IEnumerable et T pourrait être IPerson ou ICustomer ou IOrder, vous l'obtenez? – Elisabeth

+2

@Lisa - Mais en voulant avoir des méthodes _specific_ pour appeler votre paramètre générique, vous détruisez déjà la nature générique de la fonction. S'il vous plaît expliquer _why_ plusieurs surcharges ne fonctionneront pas mieux qu'une fonction générique bastardized? – Oded

3

Ajout à la réponse de Dan, Func<T, TProperty> selector dit simplement que selector est un identificateur pour une méthode qui prend un paramètre de type T et a un type de retour de TProperty. Ainsi, une méthode valable qui pourrait être passé dans BindElements comme second paramètre serait, par exemple,

string CreatePersonElement(IPerson person) { 
    return string.Format("{0} {1}", person.FirstName, person.LastName); 
} 

Dans ce cas, TProperty serait un string et T serait IPerson. Vous pouvez ensuite appeler BindElements comme si

BindElements(myPersonCollection,CreatePersonElement); 

où myPersonCollection peut être juste ce que List<T> vous faisiez allusion.Puis de passer à la boucle foreach

foreach (T item in dataObjects) { 
    // Notice: by delegating the only type-specific aspect of this method 
    // (the property) to (fittingly enough) a delegate, we are able to 
    // package MOST of the code in a reusable form. 
    var property = selector(item); 

    InlineUIContainer uiContainer = this.CreateElementContainer(property) 
    para.Inlines.Add(uiContainer); 
} 

property est en cours d'un objet de type TProperty, qui dans le cas de CreatePersonElement est un string. Si un string ne fonctionne pas pour vous, il suffit de changer le type de retour de la méthode pour être ce que CreateElementContainer accepte comme paramètre.

Vous auriez alors l'une de ces méthodes à passer dans le deuxième paramètre pour BindElements pour chaque type que vous souhaitez prendre en charge (par exemple, ICustomer, IOrder).

+0

pas la solution bitxwise, mais vaut un gros point: P merci pour l'échantillon! – Elisabeth

+0

Vous êtes les bienvenus - et oui, Dan mérite certainement crédit pour la solution =) – bitxwise