tags:

views:

94

answers:

5

I have this property, which is working fine:

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

However, it's causing me to have to repeat the ugly-looking IEnumerable<IGrouping<MessageType, Message>> in several places in my code. I tried to make this easier on the eyes by defining a trivial wrapper interface like this:

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

and changing the property above to:

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

This builds fine, but at runtime I get:

Unable to cast object of type 'System.Linq.GroupedEnumerable`3[Message,MessageType,Message]' to type 'IMessageGroups'.

(project-specific namespaces removed from the error message)

What can I do to fix this?

+3  A: 

You could try using a type alias:

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

And then:

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

Another possibility is to expand further your LINQ query and select some custom type you create after the grouping:

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

and Foo:

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

If you don't care about lazy evaluation you could put a .ToArray() at the end of your LINQ query and the return type becomes simply Foo[].

Darin Dimitrov
The problem with that solution is that I have to repeat the "using" in every scope I use the alias. I tried just doing the alias in the place where I define MessageType and Message but it is not seen elsewhere
JoelFan
Yes that's correct.
Darin Dimitrov
+2  A: 

It builds fine because the compiler can never know for sure whether the returned type isn't actually a subclass that implements the interface. (Contrast this with a cast to a class instead and you get a compiler error since that is statically known.)

But the fact remains that you are trying to cast the result of the LINQ expression into an interface that it does not implement (the "wrapper" interface). That simply isn't going to work. And nothing you can really do to fix it short of declaring a class that implements it yourself, and actually doing the "wrapping" in your implementation (passing in the existing LINQ expression perhaps to the constructor).

Kirk Woll
A: 

Oops, seems I was thinking in C++ for a C# issue. Happens when you've done a lot of different techs over a long stretch of time. Sorry to see anyone offended :D

Jas
I do not think this question is about the language that you think it is...
Kirk Woll
C# has typedef??
JoelFan
sorry C# does not have typedef, see http://stackoverflow.com/questions/161477/equivalent-of-typedef-in-c
Ian Ringrose
A: 

At the top of your cs file:

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

Source

or in your case:

using ShortName = IEnumerable<IGrouping<MessageType, Message>>;
WernerCD
Ok, but to be clear, at the top of *every* cs file that wishes to partake of this convenience.
Kirk Woll
Yeah... what I'm trying to achieve is avoiding repeating that ugly interface name at all
JoelFan
Once at the top... vs repeatedly throughout the file. progress is measured in baby steps
WernerCD
+1  A: 

The cast to IMessageGroups fails because the result of the query is an instance of IEnumerable<IGrouping<MessageType, Message>>, not an instance of IMessageGroups. But you could write a MessageGroups class and use it to wrap the result of the query :

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

And use it like that:

public IMessageGroups MessageGroups
{
    get
    {
        return
            MessageGroups.Create(
                from msg in _messages
                orderby msg.Type descending
                group msg by msg.Type);
    }
}
Thomas Levesque
Thanks... You could improve it more by adding the alias suggested by others, and you can also make the constructor private
JoelFan