2009-06-26 9 views
1

J'ai demandé à question d'utiliser la méthode a Linq qui renvoie un objet (First, Min, Max, etc.) à partir d'une collection générique. Je veux maintenant pouvoir utiliser la méthode Except() de linq et je ne sais pas trop comment le faire. Peut-être que la réponse est juste en face de moi mais pense que j'ai besoin d'aide.
J'ai une méthode générique qui remplit les dates manquantes pour un champ descriptif correspondant. Cette méthode est déclarée comme suit:Utilisation de Except() sur une collection générique

public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, string datePropertyName, string descriptionPropertyName) 
    { 
     Type type = typeof(T); 
     PropertyInfo dateProperty = type.GetProperty(datePropertyName); 
     PropertyInfo descriptionProperty = type.GetProperty(descriptionPropertyName); 
     ... 
    } 

Ce que je veux accomplir est cela. datePropertyName est le nom de la propriété date que j'utiliserai pour remplir mes espaces de dates (en ajoutant des instances d'objet par défaut pour les dates qui ne sont pas déjà présentes dans la collection). Si je traitais avec une classe non générique, je ferais quelque chose comme ceci:

foreach (string description in descriptions) 
{ 
    var missingDates = allDates.Except(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList()); 
... 
} 

Mais comment puis-je faire la même chose en utilisant les FillInMissingDates de méthode générique avec les propriétés dateProperty et descriptionProperty résolus dans l'exécution?

+1

Vous aurez besoin de nous montrer la définition de votre classe générique (ou au moins les bits pertinents) avant que nous puissions répondre correctement. – Noldorin

+0

Noldorin, le type est T, comme dans la déclaration de méthode générique FillInMissingDates. –

+0

@Gustavo: Je ne suis pas sûr de ce que vous voulez dire. Si vous ne restreignez pas le type générique T, vous ne pouvez pas garantir l'existence de la propriété datePropertyName. – Noldorin

Répondre

3

Je pense que la meilleure façon serait de définir une interface avec toutes les propriétés que vous voulez utiliser dans votre méthode. Avoir les classes que la méthode peut être utilisée dans implémenter cette interface. Ensuite, utilisez une méthode générique et contraignez le type générique à dériver de l'interface.

Cet exemple peut ne pas faire exactement ce que vous voulez - il remplit les dates manquantes pour les éléments de la liste correspondant à une description, mais j'espère que cela vous donnera l'idée de base.

public interface ITransactable 
{ 
    string Description { get; } 
    DateTime? TransactionDate { get; } 
} 

public class CompletedTransaction : ITransactable 
{ 
    ... 
} 

// note conversion to extension method 
public static void FillInMissingDates<T>(this IEnumerable<T> collection, 
              string match, 
              DateTime defaultDate) 
     where T : ITransactable 
{ 
     foreach (var trans in collection.Where(t => t.Description = match)) 
     { 
      if (!trans.TransactionDate.HasValue) 
      { 
       trans.TransactionDate = defaultDate; 
      } 
     } 
} 

Vous aurez besoin de votre énumération Cast ITransactable avant d'appeler (au moins jusqu'à ce que C# 4.0 sort).

var list = new List<CompletedTransaction>(); 

list.Cast<ITransactable>() 
    .FillInMissingDates("description",DateTime.MinValue); 

Sinon, vous pouvez étudier en utilisant Dynamic LINQ de la collection VS2008 Samples. Cela vous permettrait de spécifier le nom d'une propriété si elle n'est pas cohérente entre les classes. Cependant, vous devrez probablement utiliser la réflexion pour définir la propriété.

+0

Salut tvanfosson. Merci pour votre réponse. Je ne voudrais pas utiliser une interface car elle n'a peut-être pas la capacité d'interface avec la définition de classe. Comme je l'ai expliqué dans la question (après avoir édité), j'utilise la réflexion pour obtenir les propriétés réelles en fonction de leurs noms. Merci! –

0
foreach (string description in descriptions) 
{  
var missingDates = allDates.Except<YourClass>(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList()); 
} 

En fait, presque toutes les extensions LINQ en C# ont une valeur générique possible. (Sauf et Sauf)

+0

Ce n'est pas ce dont il parle je pense. Il signifie que * YourClass * est générique, pas la méthode d'extension Except. Il montre même dans son exemple que l'inférence de type rend cela inutile dans son cas. – Noldorin

+0

Vous avez raison Noldorin. Je ne sais pas quel est le nom de la propriété Description (donc, le paramètre descriptionPropertyName). J'ai ajouté plus d'informations à la question. Merci. –

0

Si vous voulez identifier la propriété à laquelle accéder par un nom de chaîne, vous n'avez pas besoin d'utiliser de génériques. Leur seul but est la sécurité de type statique. Il suffit d'utiliser la réflexion pour accéder à la propriété, et faire fonctionner la méthode sur un IEnumerable non-générique.

+0

C'est probablement plus ce qu'il cherche, mais j'attends toujours des éclaircissements. – Noldorin

1

Vous pouvez essayer cette approche:

public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, 
    Func<T, DateTime> dateProperty, Func<T, string> descriptionProperty, string desc) 
{ 
    return collection.Except(collection 
     .Where(d => descriptionProperty(d) == desc)) 
     .Select(d => dateProperty(d)); 
} 

Cela vous permet de faire des choses comme:

someCollection.FillInMissingDates(o => o.CreatedDate, o => o.Description, "matching"); 

Notez que vous ne devez pas nécessairement l'appel Except(), et juste avoir:

.. Where(d => descriptionProperty(d) != desc) 
0

Obtenir Sauf résultat avec plusieurs propriétés de travail avec la classe de données personnalisée n'est pas autorisé éd. Vous devez l'utiliser comme ceci: (donné msdn 101 LINQ Samples)

public void Linq53() 
{ 
    List<Product> products = GetProductList(); 
    List<Customer> customers = GetCustomerList(); 

    var productFirstChars = 
     from p in products 
     select p.ProductName[0]; 
    var customerFirstChars = 
     from c in customers 
     select c.CompanyName[0]; 

    var productOnlyFirstChars = productFirstChars.Except(customerFirstChars); 

    Console.WriteLine("First letters from Product names, but not from Customer names:"); 
    foreach (var ch in productOnlyFirstChars) 
    { 
     Console.WriteLine(ch); 
    } 
} 

Avoir la clé, vous pouvez gérer vos données en conséquence :)

Questions connexes