Динамический тип возврата из функции ввода

Мне нравится реализовывать метод с TResult, зависит от возвращаемого значения входной функции lampdacode (как и System.Linq.Select), например:

public TResult Interact<TSource> (Func<TSource, TResult> lampdaCode)

Звонок должен выглядеть так:

bool isInit = providerService.Interact<Foobar>(x => x.FoobarInit());

с bool FoobarInit() как часть класса Foobar.

однако лучшее, что я могу сделать, это определить TResult как универсальный:

public TResult Interact<TSource, TResult> (Func<TSource, TResult> lampdaCode)

Любые идеи, как добиться этого функционального вызова без необходимости передавать тип вывода в вызов Interact?

РЕДАКТИРОВАТЬ: хорошо, я думаю, что я не совсем ясно,

providerService.Interact<Foobar>(x => x.FoobarInit());

провайдер serviceservice содержит экземпляр типа Foobar и много других инстансов. Мне нравится давать нашим разработчикам возможность взаимодействовать с экземпляром Foobar, не получая экземпляр и затем вызывая FoobarInit().

например, в сценариях WCF это было бы весьма полезно.

Разработчики должны взаимодействовать с экземпляром foobar через

providerService.Interact<Foobar>(x => x.FoobarInit());

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

Ошибка CS0246 Не удалось найти тип или имя пространства имен "TResult" (отсутствует директива using или ссылка на сборку?)

3 ответа

Решение

Любые идеи, как добиться этого функционального вызова без необходимости передавать тип вывода в вызов Interact?

Нет, Interact необходимо объявить универсальный тип TResult, Правильная сигнатура метода на самом деле:

public TResult Interact<TSource, TResult> (
    Func<TSource, TResult> lampdaCode)

Звонок должен выглядеть так

Нет, не должно, в идеале это должно выглядеть так:

var isInit = providerService.Interact(x => x.FoobarInit());

Компилятор C# имеет очень хороший алгоритм вывода типов, используйте его, нет необходимости явно указывать универсальные типы в вашем вызове.

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

class Foo { public bool Frob() { ... } } 
class Blah { public int Frob() { ... } }

var res = providerService.Interact(x => x.Frob());

Хорошо, так что x сейчас? А также res? Вы можете легко обойти эту проблему, явно объявив тип параметра лямбды:

var res = providerService.Interact((Blah x) => x.Frob());

Теперь компилятор сам рассудит, что TSource является Blah а также TResult является int,

Короче говоря, C# не допускает частичного вывода обобщенных типов, но он вам не нужен в вашем сценарии. Вы можете достичь полного вывода обоих TSource а также TResult просто объявив тип в лямбда-выражении.

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

Вместо того, чтобы звонить так:

bool isInit = providerService.Interact<Foobar>(x => x.FoobarInit());

Вы можете явно указать параметр lampdaCode [SIC] и иметь вывод типа обрабатывать TResult тип:

bool isInit = providerService.Interact((Foobar x) => x.FoobarInit());

Если ты хочешь Interact чтобы можно было узнать тип из подписи, вам нужно будет предоставить его хотя бы в одном месте, где-нибудь. Прямо сейчас он понятия не имеет, что x это Foobar, поэтому он ничего не может сделать о звонке.

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

class ProviderService
{
    public TResult Interact<TInput,TResult> (Func<TInput, TResult> lambdaCode)  where TInput : class, new()
    {
        var x = new TInput();
        Initialize(x); //or whatever
        return lambdaCode(x);
    }
}

public static void Test()
{
    var providerService = new ProviderService();
    var isInit = providerService.Interact((Foobar x) => x.FoobarInit());
}

Аргументы типа не нужны.

Проверьте Пример 2 в моем DotNetFiddle для доказательства

Другие вопросы по тегам