2010-10-04 3 views
0

J'ai cette propriété, qui fonctionne très bien:ne peut pas réduire l'interface

public IEnumerable<IGrouping<MessageType, Message>> MessageGroups 
{ 
    get 
    { 
     return 
      (from msg in _messages 
       orderby msg.Type descending 
       group msg by msg.Type); 
    } 
} 

Cependant, il me cause d'avoir à répéter le laid prospectifs IEnumerable<IGrouping<MessageType, Message>> en plusieurs endroits dans mon code. J'ai essayé de rendre cela plus facile sur les yeux en définissant une interface wrapper trivial comme ceci:

public interface IMessageGroups : 
    IEnumerable<IGrouping<MessageType, Message>> { } 

et modification de la propriété ci-dessus pour:

public IMessageGroups MessageGroups 
{ 
    get 
    { 
     return 
      (IMessageGroups) 
      (from msg in _messages 
       orderby msg.Type descending 
       group msg by msg.Type); 
    } 
} 

Cela se construit bien, mais lors de l'exécution que je reçois:

Impossible de jeter l'objet de type 'System.Linq.GroupedEnumerable`3 [message MessageType, message]' taper 'IMessageGroups'.

(namespaces spécifiques au projet retirés du message d'erreur)

Que puis-je faire pour résoudre ce problème?

+0

Vous devez renvoyer un objet 'MessageGroups' qui met en oeuvre la Interface 'IMessageGroups'. 'IEnumerable >' n'implémente pas l'interface 'IMessageGroups'. – dtb

+0

Il me semble que vous pourriez éviter complètement le problème en ne l'exposant pas comme une propriété publique. Exposez simplement une propriété IEnumerable et autorisez votre code client à exécuter une requête LINQ simple. À mon avis, il n'y a pas grand intérêt à encapsuler une opération de regroupement simple, car les résultats seront presque toujours utilisés dans le cadre d'une autre requête, auquel cas vous pourriez tout aussi facilement générer une nouvelle requête de regroupement – cordialgerm

Répondre

3

Vous pouvez essayer d'utiliser un type alias:

using Foo = IEnumerable<IGrouping<MessageType, Message>>; 

Et puis:

public Foo MessageGroups 
{ 
    get 
    { 
     return 
      (from msg in _messages 
      orderby msg.Type descending 
      group msg by msg.Type); 
    } 
} 

Une autre possibilité est d'élargir davantage votre requête LINQ et sélectionnez un certain type personnalisé que vous créez après le regroupement:

public IEnumerable<Foo> MessageGroups 
{ 
    get 
    { 
     return 
      (from msg in _messages 
      orderby msg.Type descending 
      group msg by msg.Type 
      select new Foo { Messages = g, MessageType = g.Key }  
      ); 
    } 
} 

a nd Foo:

public class Foo 
{ 
    public MessageType MessageType { get; set; } 
    public IEnumerable<Message> Messages { get; set; } 
} 

Si vous ne se soucient pas de l'évaluation paresseuse vous pouvez mettre un .ToArray() à la fin de votre requête LINQ et le type de retour devient tout simplement Foo[].

+0

Je dois répéter le "using" dans chaque portée j'utilise l'alias. J'ai essayé juste de faire l'alias à l'endroit où je définis MessageType et Message mais il n'est pas vu ailleurs – JoelFan

+0

Oui c'est correct. –

2

Il se construit bien parce que le compilateur ne peut jamais savoir avec certitude si le type retourné n'est pas réellement une sous-classe qui implémente l'interface. (Contraste avec un casting à une place classe et vous obtenez une erreur du compilateur depuis est connu statiquement.)

Mais le fait demeure que vous essayez de jeter le résultat de l'expression LINQ dans une interface que n'implémente pas (l'interface "wrapper"). Cela ne va tout simplement pas fonctionner. Et rien de ce que vous pouvez vraiment faire pour le corriger, à moins de déclarer une classe qui l'implémente vous-même, et de faire le "wrapping" dans votre implémentation (en passant peut-être l'expression LINQ existante au constructeur).

0

En haut de votre fichier cs:

using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>; 

Source

ou dans votre cas:

using ShortName = IEnumerable<IGrouping<MessageType, Message>>; 
+1

Ok, mais pour être clair, en haut de chaque * fichier * cs qui souhaite prendre part à cette commodité. –

+0

Ouais ... ce que j'essaie de réaliser est d'éviter de répéter ce nom d'interface laide du tout – JoelFan

+0

Une fois au sommet ... vs à plusieurs reprises dans le fichier. le progrès est mesuré dans les étapes de bébé – WernerCD

1

Le casting de IMessageGroups échoue parce que le résultat de la requête est une instance de IEnumerable<IGrouping<MessageType, Message>>, pas une instance de IMessageGroups. Mais vous pourriez écrire une classe MessageGroups et l'utiliser pour envelopper le résultat de la requête:

public class MessageGroups : IMessageGroups 
{ 
    private readonly IEnumerable<IGrouping<MessageType, Message>> _groups; 

    public MessageGroups(IEnumerable<IGrouping<MessageType, Message>> groups) 
    { 
     _groups = groups; 
    } 

    public IEnumerable<IGrouping<MessageType, Message>> GetEnumerator() 
    { 
     return _groups.GetEnumerator(); 
    } 

    public static MessageGroups Create(IEnumerable<IGrouping<MessageType, Message>> groups) 
    { 
     return new MessageGroups(groups); 
    } 
} 

et de l'utiliser comme ça:

public IMessageGroups MessageGroups 
{ 
    get 
    { 
     return 
      MessageGroups.Create(
       from msg in _messages 
       orderby msg.Type descending 
       group msg by msg.Type); 
    } 
} 
+0

Merci ... Vous pourriez l'améliorer plus en ajoutant l'alias suggéré par d'autres, et vous pouvez également rendre le constructeur privé – JoelFan