Почему C# не выводит мои общие типы?


у меня есть много Funcy fun (fun intended) с общими методами. В большинстве случаев вывод типа C# достаточно умен, чтобы выяснить, какие общие аргументы он должен использовать в моих общих методах, но теперь у меня есть дизайн, в котором компилятор C# не преуспевает, хотя я считаю, что ему удалось бы найти правильные типы.

может ли кто-нибудь сказать мне, является ли компилятор немного тупым в этом случае, или есть очень ясная причина, почему он не может вывести мой общий аргументы?

вот код:

классы и определения интерфейса:

interface IQuery<TResult> { }

interface IQueryProcessor
{
    TResult Process<TQuery, TResult>(TQuery query)
        where TQuery : IQuery<TResult>;
}

class SomeQuery : IQuery<string>
{
}

код, который не компилируется:

class Test
{
    void Test(IQueryProcessor p)
    {
        var query = new SomeQuery();

        // Does not compile :-(
        p.Process(query);

        // Must explicitly write all arguments
        p.Process<SomeQuery, string>(query);
    }
}

почему это? Чего мне здесь не хватает?

вот сообщение об ошибке компилятора (он не оставляет много для нашего воображения):

аргументы типа для метода IQueryProcessor.Процесс (TQuery) не может быть выведен из использования. Попробуйте указать аргумент типа явно.

причина, по которой я считаю, что C# должен быть в состоянии сделать это, заключается в следующем:

  1. я поставляю объект, который реализует IQuery<TResult>.
  2. только IQuery<TResult> версия, которую реализует тип IQuery<string> и таким образом TResult должен быть string.
  3. С этой информацией компилятор имеет TResult и TQuery.

решение

для меня лучшим решением было изменить элемент IQueryProcessor интерфейс и использование динамического ввода в реализации:

public interface IQueryProcessor
{
    TResult Process<TResult>(IQuery<TResult> query);
}

// Implementation
sealed class QueryProcessor : IQueryProcessor {
    private readonly Container container;

    public QueryProcessor(Container container) {
        this.container = container;
    }

    public TResult Process<TResult>(IQuery<TResult> query) {
        var handlerType =
            typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
        dynamic handler = container.GetInstance(handlerType);
        return handler.Handle((dynamic)query);
    }
}

The IQueryProcessor интерфейс теперь принимает в

4   51   2011-12-15 00:17:43

4 ответа:

группа людей указала, что C# не делает выводов на основе ограничений. Это правильно, и имеет отношение к вопросу. Выводы делаются путем изучения аргументы и их типы формального параметра и это единственный источник информации вывода.

куча людей тогда связались с этим статья:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

эта статья устарела и не имеет отношения к вопросу. Он устарел, потому что он описывает дизайнерское решение, которое мы сделали в C# 3.0, которое мы затем отменили в C# 4.0, в основном на основе ответа на эту статью. Я добавил обновленную информацию в статье.

не имеет значения, потому что статья о вывод типа возврата от аргументов группы методов к формальным параметрам универсального делегата. Это не та ситуация, о которой спрашивает оригинальный плакат.

соответствующая статья, которую я читаю, скорее это:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

C# не будет выводить универсальные типы на основе возвращаемого типа универсального метода, только аргументы метода.

Он также не использует ограничения как часть вывода типа, что исключает общее ограничение от предоставления типа для вас.

Подробнее см. сообщение Эрика Липперта на эту тему.

он не использует ограничения для вывода типов. Скорее он выводит типы (когда это возможно), а затем проверяет ограничения.

поэтому пока единственно возможный TResult Это может быть использовано с SomeQuery параметр, он этого не увидит.

Обратите также внимание, что это было бы вполне возможно для SomeQuery о проведении IQuery<int> - Это одна из причин, почему это ограничение компилятора не может быть плохой идеей.

спецификация излагает это довольно четко:

Раздел 7.4.2 Вывод Типа

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

Tr M (T1 x1 ... Tm xm)

при вызове метода вида M (E1 ...Em) задача вывода типа заключается в найти уникальные аргументы типа S1...Sn для каждого из параметров типа X1 ... Xn так, что вызов M (E1...Em)становится допустимым.

как вы можете видеть, возвращаемый тип не используется для определения типа. Если вызов метода не сопоставляется непосредственно с аргументами типа, вывод немедленно завершается ошибкой.

компилятор не просто предполагает, что вы хотели string Как TResult аргумент, и он не может. Представьте себе TResult выведена из строки. Оба будут действительны, так что выбрать? Лучше чтобы быть откровенным.