Как сделать select динамическим

Ответ

Для динамического select можно использовать reflect.Select. Он позволяет создавать case-ы во время выполнения. Пример:

cases := make([]reflect.SelectCase, 0)

// Добавляем case для канала
ch := make(chan int)
cases = append(cases, reflect.SelectCase{
    Dir:  reflect.SelectRecv,
    Chan: reflect.ValueOf(ch),
})

// Добавляем case с default
cases = append(cases, reflect.SelectCase{
    Dir:  reflect.SelectDefault,
})

// Выполняем select
chosen, recv, recvOK := reflect.Select(cases)

// Обработка результата
switch chosen {
case 0:
    if recvOK {
        fmt.Printf("Получено: %vn", recv.Interface())
    } else {
        fmt.Println("Канал закрыт")
    }
case 1:
    fmt.Println("Default case сработал")
}

Ключевые моменты:

  1. reflect.SelectCase описывает каждый case.
  2. Dir указывает направление: reflect.SelectRecv (получение), reflect.SelectSend (отправка) или reflect.SelectDefault (default-ветка).
  3. Chan должно быть reflect.ValueOf канала.
  4. Send (для отправки) и Recv (для получения) должны быть reflect.ValueOf значений.
  5. Можно динамически добавлять/удалять case-ы в слайсе cases.
  6. reflect.Select возвращает индекс выбранного case (chosen), полученное/отправленное значение (recv) и булево значение recvOK (для получения).

Осторожно: reflection снижает читаемость и производительность. Используйте только когда действительно нужна динамичность, например, при построении обобщенных библиотек или фреймворков, где количество и тип каналов неизвестны заранее.