6

I've written an extension method on IQueryable which returns the same type of IQueryable, just filtered a bit. Let's say it's something like that:

public static IEnumerable<T> Foo<T>(this IEnumerable<T> source, int? howmany = null)
{
    if (howmany.HasValue)
        return source.Take(howmany.Value);
    return source;
}

calling the above method is as simple as someLinqResult.Foo(2)

I need other method that will return instance of other generic class, but with other base class (not the same as source T above). So, here's the code, let's assume this method is supposed to return a list of given type (other than the input type which IQueryable has!) but the same length [the actual problem is about transforming NHibernate query results, but it doesn't matter):

public static List<TTarget> Bar<TTarget,TSource>(this IEnumerable<TSource> source)
{
    return new List<TTarget>(source.Count());
}

now, given that the strLinqResult is IQueryable<string>, I need to call it strLinqResult.Bar<int,string>();.

The point is I have to pass both types, even though the first one is already known as I'm calling the method on already defined IQuerable.

Since it was enough to call Foo(2) and not Foo<string>(2) I thought the compiler is able to "pass/guess" the type automatically.

So why do I need to call the second method Bar<int,string>(); and not just Bar<int>() ?


the actual code:

    public static ListResponse<TResponse> 
        BuildListResponse<T,TResponse>(this IQueryable<T> iq, ListRequest request)
        where TResponse: new()
    {
        var result = iq.ApplyListRequestParams(request).ToList().ConvertAll(x => x.TranslateTo<TResponse>());
        var tcount = iq.Count();
        return new ListResponse<TResponse> {
                Items =  result,
                _TotalCount = tcount,
                _PageNumber = request._PageNumber ?? 1,
            };
    }

ApplyListRequestParams is kind of Foo method from the example code - it just applies pagination & ordering params available in ListRequest object.

Items is a public List<T> Items in a class ListResponse<T>.

TranslateTo is a method from ServiceStack.

The above method called on IQueryable<T> returned by NHibernate (T is Domain Model) takes the request parameters (ordering, pagination), applies them, and then transforms the results list from DomainModel to DTO Object of type TResponse. The list is then wrapped in a generic response class (generic, so it is reusable for many DTO types)

migajek
  • 8,524
  • 15
  • 77
  • 116
  • Have you tried switching the definition to `Bar(...)`? – PinnyM Jun 16 '13 at 19:52
  • @PinnyM yep, I did. it still says I need to pass 2 type parameters – migajek Jun 16 '13 at 19:54
  • @Jon I might have misunderstood the other solution, but extending the actual class is out of question - it needs to be the extension method! – migajek Jun 16 '13 at 20:03
  • @migajek: The linked answer suggests using a static method on a separate generic class (not the one you are working with). Alternatively you could arrange for the call to be made with extension method syntax using an intermediate type, but that would take *two* extension method calls. Using extension method syntax with just one call requires the compiler to infer just one of the type arguments, which it does not do. – Jon Jun 16 '13 at 20:08
  • @Jon thanks, so the simple and short answer is "it's impossible". Yet I don't find it the same question as mentioned by you, as the solution of the mentioned problem doesn't apply here. – migajek Jun 16 '13 at 20:14
  • @migajek: It applies 100%, only the syntax changes from `x.Foo` to `NewClassCreatedForThisPurpose.Foo(x)`. That's the best you can do. – Jon Jun 16 '13 at 20:16
  • @Jon the compiler says I can't define extension method on a generic class. – migajek Jun 16 '13 at 20:18
  • @Jon, sorry - now I see the difference. Thank you! – migajek Jun 16 '13 at 20:18
  • 1
    Suppose you added a `Bar(this IEnumerable source)`. If the compiler accepted your shorthand, then it would now be unambiguous whether `strLinqResult.Bar();` referred to `Bar` or `Bar`. I think this is what Johny Skovdal is getting at in his answer, but I wanted to spell it out. – Tim Goodman Jun 16 '13 at 20:34
  • Tim, thank you. I'm newbie at C# and generic programming, so this wasn't obvious to me. – migajek Jun 16 '13 at 20:55
  • You're welcome. Of course my comment should say "it would now be ambiguous", not "unambiguous"... but you got what I meant. :) – Tim Goodman Jun 16 '13 at 22:05

1 Answers1

3

If I'm understanding your problem correctly, you're attempting to input as few parameters as possible. That's just not possible. It's all or none where it can figure all out on its own.

The problem with only specifying some, is that you will the be able to introduce a similar method with the 1 less type parameter, and will now have broken the implementation.

But any chance you could share your original problem as well? Might be able to let the compiler figure it out some other way?

Johny Skovdal
  • 2,038
  • 1
  • 20
  • 36