How to create an extension method with the same signature but different generic type?
I have the following extension methods:
public static class QueryableOptionalDateRangeExtensions
{
public static IQueryable<T> StartsFrom<T>(this IQueryable<T> query, DateTime date)
where T : IOptionalDateRange // Might also be IRequiredDateRange
{
return query.Where(obj => obj.Start >= date);
}
public static IQueryable<T> StartsUntil<T>(this IQueryable<T> query, DateTime date)
where T : IOptionalDateRange // Might also be IRequiredDateRange
{
return query.Where(obj => obj.Start < date);
}
public static IQueryable<T> EndsUntil<T>(this IQueryable<T> query, DateTime date)
where T : IOptionalDateRange
{
return query.Where(obj => obj.End <= date);
}
}
public static class QueryableRequiredDateRangeExtensions
{
public static IQueryable<T> StartsFrom<T>(this IQueryable<T> query, DateTime date)
where T : IRequiredDateRange
{
开发者_如何学Go return query.Where(obj => obj.Start >= date);
}
public static IQueryable<T> StartsUntil<T>(this IQueryable<T> query, DateTime date)
where T : IRequiredDateRange
{
return query.Where(obj => obj.Start < date);
}
public static IQueryable<T> EndsUntil<T>(this IQueryable<T> query, DateTime date)
where T : IRequiredDateRange
{
return query.Where(obj => obj.End <= date);
}
}
This however does not work since it can't infer the overloading from the type of T for some reason (although it seems possible to me).
What can be done to work around this issue?EDIT:
Here's the IDateRange interface:public interface IDateRange<TStart, TEnd>
{
TStart Start { get; set; }
TEnd End { get; set; }
}
It just specifies that the class has a start and an end. Now I want it to specify whether the object has an optional date range (both start and end are nullable) or a required date range (both are value types) but the same extension method must work on both and I don't really want to specify the Start and End properties' types.
It's not clear what you mean by "does not work". Were you expecting overload resolution (including finding extension methods) to take account of the constraints? If so, that doesn't happen - I've written a blog post going into the details of this.
That's possibly why what you're trying isn't working - although as you haven't given an example, it's hard to say for sure. As for how to fix it - I would suggest using different method names, e.g. MaybeStartsFrom
for the optional range. If you could give a short but complete example of what you're trying to achieve, that would help...
Make both IRequiredDateRange
and IOptionalDateRange
inherit from IDateRange
and put the Start
and End
properties there. What you are trying can't be achieved otherwise, because "generic method type inference deliberately does not make any deductions from the constraints", see here.
First, it T is always "IOptionalDateRange" in the methods of QueryableOptionalDateRangeExtensions, why make it generic in the first place?
Second, it seems the IOptionalDateRange interface simply extends the IRequiredDateRange interface (I guessed that because your wrote IOptionalDateRange might also be IRequiredDateRange). That means, if an object implements IOptionalDateRange, it also implements IRequiredDateRange. How do you expect the compiler to differentiate?
So solve this, the interface should not inherit each other, instead they could share a common base class.
If you are on .NET 4 and implement the interfaces with classes (not structures), you can take advantage of covariance and make the methods non-generic. Instead you would have
IQueryable<IOptionalDateRange> EndsUntil(this IQueryable<IOptionalDateRange> query, DateTime date)
IQueryable<IRequiredDateRange> EndsUntil(this IQueryable<IRequiredDateRange> query, DateTime date)
You could move them into separate namespaces but that would be BAD. It is fragile because changing namespaces could result in different behaviour (unexpectedly). Plus this won't work if you need to use both methods in the same file (as pointed out by Daniel).
Okay, so after reading your update why don't you add an IsDateOptional
property to your IDateRange
interface, define the date types as DateTime
getting rid of the generic element to that interface. There is now no need for two separate interfaces IOptionalDateRange
and IRequiredDateRange
... and you can replace the two extension classes with one.
public interface IDateRange
{
DateTime Start { get; set; }
DateTime End { get; set; }
bool IsDateOptional { get; }
}
public static class QueryableDateRangeExtensions
{
public static IQueryable<T> StartsFrom<T>(this IQueryable<T> query, DateTime date)
where T : IDateRange
{
return query.Where(obj => obj.Start >= date);
}
public static IQueryable<T> StartsUntil<T>(this IQueryable<T> query, DateTime date)
where T : IDateRange
{
return query.Where(obj => obj.Start < date);
}
public static IQueryable<T> EndsUntil<T>(this IQueryable<T> query, DateTime date)
where T : IDateRange
{
return query.Where(obj => obj.End <= date);
}
}
精彩评论