Как создать метод расширения с той же сигнатурой, но с другим универсальным типом? - PullRequest
2 голосов
/ 15 сентября 2011

У меня есть следующие методы расширения:

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

Он просто указывает, что у класса есть начало и конец.Теперь я хочу, чтобы он указывал, имеет ли объект необязательный диапазон дат (и начало, и конец, могут быть обнуляемыми) или требуемый диапазон дат (оба являются типами значений), но один и тот же метод расширения должен работать на обоих, и я действительно не хочуукажите типы свойств Start и End.

Ответы [ 5 ]

3 голосов
/ 15 сентября 2011

Непонятно, что вы подразумеваете под «не работает». Вы ожидали, что разрешение перегрузки (включая поиск методов расширения) будет учитывать ограничения? Если это так, этого не происходит - я написал сообщение в блоге , в котором подробно расскажу об этом.

Возможно, поэтому то, что вы пытаетесь, не работает - хотя, поскольку вы не привели пример, трудно сказать наверняка. Что касается исправления этого - я бы предложил использовать разные имена методов, например, MaybeStartsFrom для дополнительного диапазона. Если бы вы могли привести короткий, но полный пример того, чего вы пытаетесь достичь, это помогло бы ...

2 голосов
/ 15 сентября 2011

Сделайте так, чтобы IRequiredDateRange и IOptionalDateRange наследовали от IDateRange и поместили туда свойства Start и End.То, что вы пытаетесь, не может быть достигнуто иначе, потому что «вывод общего типа метода сознательно не делает какие-либо выводы из ограничений», см. здесь .

0 голосов
/ 15 сентября 2011

Если вы работаете в .NET 4 и реализуете интерфейсы с классами (не структурами), вы можете воспользоваться ковариацией и сделать методы не универсальными. Вместо этого у вас будет

IQueryable<IOptionalDateRange> EndsUntil(this IQueryable<IOptionalDateRange> query, DateTime date)
IQueryable<IRequiredDateRange> EndsUntil(this IQueryable<IRequiredDateRange> query, DateTime date)
0 голосов
/ 15 сентября 2011

Во-первых, это T всегда "IOptionalDateRange" в методах QueryableOptionalDateRangeExtensions, зачем вообще делать его универсальным?

Во-вторых, кажется, что интерфейс IOptionalDateRange просто расширяет интерфейс IRequiredDateRange (как я догадался, потому чтоВаш записанный IOptionalDateRange также может быть IRequiredDateRange).Это означает, что если объект реализует IOptionalDateRange, он также реализует IRequiredDateRange.Как вы ожидаете, что компилятор будет различать?

Так что решите это, интерфейс не должен наследовать друг друга, вместо этого они могут иметь общий базовый класс.

0 голосов
/ 15 сентября 2011

Вы можете переместить их в отдельные пространства имен, но это будет ПЛОХО. Это хрупко, потому что изменение пространств имен может привести к другому поведению (неожиданно). Плюс это не сработает, если вам нужно использовать оба метода в одном и том же файле (как указал Даниэль).


Хорошо, поэтому после прочтения вашего обновления, почему бы вам не добавить свойство IsDateOptional в свой интерфейс IDateRange, определите типы дат как DateTime, чтобы избавиться от универсального элемента для этого интерфейса. Теперь нет необходимости в двух отдельных интерфейсах IOptionalDateRange и IRequiredDateRange ... и вы можете заменить два класса расширений одним.

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