Динамический тип возврата из функции ввода
Мне нравится реализовывать метод с 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());
}
Аргументы типа не нужны.