#c# #dictionary #lambda #async-await
#c# #словарь #лямбда #async-ожидание
Вопрос:
Я пытаюсь написать этот фрагмент кода, который не компилируется (материал удален / упрощен):
private async Task<PatientChartData> GetDomainListingSummaryMeta<T>(string phn)
where T : PatientChartBase, new()
{
var getListingSummaryTypeSwitch = new Dictionary<Type, Func<????>> {
{ typeof(Documents), async () => await this.GetDocuments(phn) },
{ typeof(Encounters), async () => await this.GetEncounters(phn) },
{ typeof(Labs), async () => await this.GetLabs(phn) },
};
Task t = new Task(async () =>
{
// this is what code would look like without a switch, e.g.
// await this.GetDocuments(phn);
// but with a type switch, I want to do something like this:
await getListingSummaryTypeSwitch(typeof(T))();
});
t.Start(); // notice, we are not waiting for this task to complete
}
GetDocuments возвращает задачу, GetEncounters возвращает задачу и т. Д. Однако в моем текущем дизайне меня на самом деле не волнует ответ, поскольку я вызываю их в фоновом потоке, Который будет иметь побочные эффекты (например, помещая их в базу данных / кэш и т. Д.) Пожалуйста, не обращайте внимания на проблемы с дизайном, поскольку это фоновые потоки.
Мой вопрос действительно касается синтаксиса о том, как я заставляю это работать.
Комментарии:
1. он не компилируется… вы указали это в своем вопросе, но не говорите нам, в чем ошибка….
2. Рассматривали ли вы возможность использования полиморфизма для решения этой проблемы? Подумайте, если все ваши типы являются производными от общего типа, все они могут по-разному реализовывать абстрактный метод базового класса или общего интерфейса. Тогда вы могли бы просто вызвать этот метод и добиться поведения, зависящего от типа. Ваш словарь устареет.
3. пожалуйста, посмотрите??? в Func<> Плюс, я даже не уверен, будет ли это работать правильно и / или ожидаемо, даже если оно скомпилируется. Должен ли я использовать Action вместо Func? правильно ли используется использование async / await ?
4. Зачем вы вообще создаете здесь универсальный метод, когда вы знаете, что будет ограниченный набор типов, с которыми вы можете юридически обращаться? Почему бы просто не создать N перегрузок с соответствующими именами, имея соответствующий код? Вы должны стремиться к «открытости», способности программиста (которым можете быть вы) определять, какими будут допустимые типы и методы. Используя общий метод
T
, вы, по сути, говорите: «Этот метод может принимать любой тип», а затем после этого все это «не-а, обманул тебя».5. @lassevkarlsen прав. Если ваш общий код правильно обрабатывает только определенные типы, он не должен быть общим.
Ответ №1:
вы хотите использовать тип в качестве индексатора, а не в качестве аргумента?
await getListingSummaryTypeSwitch[typeof(T)]();
Что касается вашего Func<???>
: он должен возвращать ожидаемый, иначе вы не сможете использовать await
перед своим коммутатором. Обычно вы бы использовали Func<Task>
или что-то общее Func<Task<resulttype>>
.
Я не буду обсуждать другие недостатки (например, перенос в другую задачу), которые обсуждаются в разделе комментариев к вашему вопросу, потому что вы сказали, что упростили его.
Ответ №2:
Вот что я в итоге сделал. Как многие отмечали — этот код, хотя он и работает, ужасный дизайн. Это было просто что-то быстрое и грязное, что мы создавали прототипы. Это началось с того, что мой босс попросил 1 домен … затем он попросил еще 2 и т.д. Я не хотел реорганизовывать все свои классы, чтобы использовать соответствующие интерфейсы / полиморфизм, поскольку мы постоянно что-то меняли во внутренних реализациях, и (б) я решил, что это «общее» использование будет для меня быстрым способом предотвратить дублирование кода. Надеюсь, это поможет всем, кому это может понадобиться.
var actionSwitch = new Dictionary<Type, Action> {
{ typeof(Documents), async () => await _patientProcessorService.GetDocuments(phn) },
{ typeof(Encounters), async () => await _patientProcessorService.GetEncounters(phn) },
{ typeof(Labs), async () => await _patientProcessorService.GetLabs(phn) },
};
Task t = new Task(async () =>
{
actionSwitch[typeof(T)]();
});
t.Start();
Комментарии:
1. Для каких-либо из этих действий нет причин, для
async
await
методов нет причин. Нет причин создавать новую задачу для вызова действия, а если бы и были, это не должно бытьasync
лямбда, и вы не должны использоватьTask
конструктор для запуска задач.