Marco
Marco

Reputation: 2473

.NET Generics Question

Can someone please explain why I am getting a compile error asking for an explicit cast? Since I'm constraining the output, I thought this would be allowed without any brute force.

** THANK YOU EVERYONE. IT WAS CONFUSING THE WAY I PRESENTED THE QUESTION. **

public interface IQuery<TInput, TOutput>
{
    TOutput Execute(TInput input);
}

public abstract class PagedQuery<TInput, TOutput> : IQuery<TInput, TOutput>
    where TOutput : IEnumerable<TOutput>
{
    public TOutput Execute(TInput input)
    {
        return Enumerable.Empty<TOutput>(); // error here..
    }
}

Upvotes: 1

Views: 270

Answers (4)

Elian Ebbing
Elian Ebbing

Reputation: 19027

I think what you try to do is create a generic class PagedQuery where the output is always enumerable, while the generic IQuery interface doesn't necessarily produce enumerable ouput.

I guess you could write this to accomplish that:

// Define other methods and classes here
public interface IQuery<TInput, TOutput>
{
    TOutput Execute(TInput input);
}

public class PagedQuery<TInput, TOutput> : IQuery<TInput, IEnumerable<TOutput>>
{
    public IEnumerable<TOutput> Execute(TInput input)
    {
        return Enumerable.Empty<TOutput>();
    }
}

Now you can write

var query = new PagedQuery<int, int>();
IEnumerable<int> output = query.Execute(0);

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1499770

Enumerable.Empty<TOutput>() returns an implementation of IEnumerable<TOutput>. Just because TOutput also implements IEnumerable<TOutput> doesn't mean that you can convert any IEnumerable<TOutput> value to TOutput.

I agree with the commentators who say that constraining TOutput to return a sequence of itself is pretty odd, by the way.

I suspect you actually want something like this:

public abstract class PagedQuery<TInput, TElement>
    : IQuery<TInput, IEnumerable<TElement>>
{
    public IEnumerable<TElement> Execute(TInput input)
    {
        return Enumerable.Empty<TElement>();
    }
}

That makes a lot more sense to me.

EDIT: I've renamed the type parameter to make it clearer. So for IQuery<,>, TOutput=IEnumerable<TElement> - so a PagedQuery returns a sequence of elements, not a sequence of pages, each of which itself is a sequence of pages, each of which itself is a sequence of pages ad infinitum.

Upvotes: 9

Jeff Fritz
Jeff Fritz

Reputation: 9861

Consider moving TOutput to be the payload of the IEnumerable you're returning:

    public interface IQuery<TInput, TOutput>
    {
        IEnumerable<TOutput> Execute(TInput input);
    }

    public abstract class PagedQuery<TInput, TOutput> : IQuery<TInput, TOutput>
    {
        public IEnumerable<TOutput> Execute(TInput input)
        {
            return Enumerable.Empty<TOutput>();
        }
    }

Upvotes: 0

Christopher Currens
Christopher Currens

Reputation: 30695

The compiler is expecting you to return TOutput instead of an IEnumerable<TOutput>, it doesn't matter that your constraint specifies that TOutput inherits from IEnumerable<TOutput>, as the types aren't the same.

If you can, changing the type in the interface and abstract class on the Execute method to IEnumerable<TOutput> solves the compiler issue.

Upvotes: 3

Related Questions