Можно ли добиться ковариации в .NET 3.5, C # - PullRequest
2 голосов
/ 12 марта 2012

У меня есть class X, который реализует interface IX.У меня также есть класс репозитория, выделенный для X, который использует лямбда-выражения в качестве параметров:

public interface IX
{
}

public class X : IX
{
    ....
}

public class XRepository : IRepository<X>
{
    public IEnumerable<X> Filter(Func<X, bool> filterFunc)
    {
        ...
    }
}

Мне нужно, чтобы класс репозитория работал с интерфейсом IX, поэтому я добавляю IRepository<IX> к интерфейсамреализуется:

public class XRepository : IRepository<X>, IRepository<IX>
{
    public IEnumerable<X> Filter(Func<X, bool> filterFunc)
    {
        ...
    }
    public IEnumerable<IX> Filter(Func<IX, bool> filterFunc)
    {
        // I need to call the same filter method as above, but 
        // in order to do so I must convert the Func<IX, bool> to Func<X, bool>.
    }
}

Я должен преобразовать Func<IX, bool> в Func<X, bool>, но, поскольку код написан на C # 3.0 с использованием .NET 3.5, я не могу воспользоваться ковариацией типов, которая была введена в 4.0.

Простым решением может быть использование Func<X, bool> newFunc = x => filterFunc(x);, где filterFunc имеет тип Func<IX, bool>.Это скомпилирует, и можно ожидать, что он будет работать нормально, но я предполагаю, что это не будет.Проблема в том, что я использую стороннюю платформу для реализации фильтра, а именно FluentNhibernate.Я знаю, что он использует деревья выражений для удаления переданного в условие доступа к лямбда-выражению члена (например, x => x.Name == "John") для создания собственного запроса SQL (например, WHERE Name = 'John').Приведенное выше решение даст Func<X, bool>, которое не является таким выражением, и я боюсь, что оно не сможет перевести.Поэтому мне нужно создать то же самое лямбда-выражение, но с совместимым типом.Зная, что X реализует IX, очевидно, что любой код внутри Func<IX, bool> будет работать для объектов типа X.Однако для меня не очевидно, как я могу выполнить это преобразование.

Я предполагаю, что это можно сделать с помощью деревьев выражений.Я также боюсь, что моя работа сильно пострадает.Даже если я решу найти другое решение для моего сценария, я все равно буду признателен за предложенный способ перевода одной лямбды в похожую другую.


Редактировать:

Чтобы прояснить проблему, с которой я столкнулся, я написал следующий тест, имитирующий реальный сценарий, с которым я столкнулся:

    Func<IX, bool> filter = y => y.Name == "John";
    Func<X, bool> compatibleFilter = y => filter(y);


    ...
    // Inside the Filter(Func<X, bool> filter method)
    using(var session = nhibernateSessionFactory.OpenSession())
    {

        IEnumerable<X> xx = session.Query<X>().Where(z => compatibleFilter(z)).ToList();
    }

, поэтому при методе ToList() я получаю следующее исключение

Unable to cast object of type 'NHibernate.Hql.Ast.HqlParameter' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'.

Это подтверждает мое предположение, что Flunet NHiberante не может правильно обрабатывать аргумент compatibilityFilter.

Итак, я хочу, чтобы это был способ преобразования Func в Func или, как предложил Джон Скит, Expression<Func<IX, bool>> на Expression<Func<X, bool>>, которые имеют одинаковое тело (y => y.Name = "John").


Редактировать 2:

Наконец-то я это сделал!Правильный способ - не использовать Func<X, bool>, а Expression<Func<X, bool>>.

 Expression<Func<IX, bool>> filter = y => y.Name == "John Skeet";
 Expression<Func<X, bool>> compatibleFilter = Expression.Lambda<Func<X, bool>>(
    filter.Body, 
    filter.Parameters);

. Это дает правильный запрос SQL.IX, bool

Ответы [ 3 ]

3 голосов
/ 12 марта 2012

Простым решением может быть использование Func<X, bool> newFunc = x => filterFunc(x);, где filterFunc имеет тип Func<IX, bool>. Это скомпилирует, и можно ожидать, что он будет работать нормально, но я предполагаю, что это не будет.

Зачем предполагать, когда можно проверить Это должно работать абсолютно нормально. В конце концов, вы передаете аргумент типа X для параметра типа IX, который не вызывает проблем с безопасностью типов.

Затем вам необходимо преобразовать из IEnumerable<X> в IEnumerable<IX>, что можно сделать с помощью Cast, например:

public IEnumerable<IX> Filter(Func<IX, bool> filterFunc)
{
    Func<X, bool> newFilter = x => filterFunc(x);
    return Filter(newFilter).Cast<IX>();
}
0 голосов
/ 12 марта 2012

почему вы используете свой конкретный тип, разве IX не достаточно:

public class IXRepository : IRepository<IX>
{
    public IEnumerable<X> Filter(Func<IX, bool> filterFunc)
    {
        ...
    }
}
0 голосов
/ 12 марта 2012

Насколько я понимаю, ковариация - это особенность языка.Так что это не зависит напрямую от .net 4.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...