如何创建具有相同签名但不同泛型类型的扩展方法?

时间:2011-09-15 12:25:33

标签: c# .net generics 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
    {
        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);
    }
}

然而这不起作用,因为它无法从某种原因推断出T类型的过载(尽管我觉得可能)。
可以采取哪些措施解决这个问题?

编辑:
这是IDateRange界面:

public interface IDateRange<TStart, TEnd>
{
    TStart Start { get; set; }
    TEnd End { get; set; }
}

它只是指定类具有开始和结束。 现在我希望它指定对象是否具有可选的日期范围(开始和结束都可以为空)或所需的日期范围(两者都是值类型)但是相同的扩展方法必须同时适用于两者并且我真的不想指定开始和结束属性的类型。

5 个答案:

答案 0 :(得分:3)

“不行”并不清楚你的意思。您是否期望重载解析(包括查找扩展方法)以考虑约束?如果是这样,那就不会发生 - 我已经写了blog post进入详细信息。

这可能就是为什么你所尝试的不起作用 - 尽管你没有举例说明,但很难肯定。至于如何修复它 - 我建议使用不同的方法名称,例如可选范围MaybeStartsFrom。如果你能给出一个简短而完整的例子,说明你想要实现的目标,那将会有所帮助......

答案 1 :(得分:2)

IRequiredDateRangeIOptionalDateRange继承IDateRange并将StartEnd属性放在那里。您正在尝试的其他方法无法实现,因为“通用方法类型推断故意从约束中扣除”,请参阅here

答案 2 :(得分:0)

您可以将它们移动到单独的命名空间中,但这样会很糟糕。它很脆弱,因为更改名称空间可能会导致不同的行为(意外)。如果你需要在同一个文件中使用这两种方法(如Daniel所指出的那样),这将无效。


好的,所以在阅读完更新后为什么不向IsDateOptional接口添加IDateRange属性时,请将日期类型定义为DateTime除去通用元素接口。现在不需要两个单独的接口IOptionalDateRangeIRequiredDateRange ...并且您可以用一个替换两个扩展类。

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

答案 3 :(得分:0)

首先,在QueryableOptionalDateRangeExtensions的方法中,T总是“IOptionalDateRange”,为什么首先使它成为通用的?

其次,似乎IOptionalDateRange接口只是扩展了IRequiredDateRange接口(我猜测因为你写的IOptionalDateRange也可能是IRequiredDateRange)。这意味着,如果一个对象实现了IOptionalDateRange,它还会实现IRequiredDateRange。您期望编译器如何区分?

所以解决这个问题,接口不应该相互继承,而是可以共享一个公共基类。

答案 4 :(得分:0)

如果您使用的是.NET 4并使用类(而不是结构)实现接口,则可以利用协方差并使方法非通用。相反,你会有

IQueryable<IOptionalDateRange> EndsUntil(this IQueryable<IOptionalDateRange> query, DateTime date)
IQueryable<IRequiredDateRange> EndsUntil(this IQueryable<IRequiredDateRange> query, DateTime date)