Аргумент func с массивом смешанных типов (протокол), затем вызовите статический метод протокола

Вот мои структуры (у меня может быть гораздо больше, поэтому мне нужен общий способ работы с MyProtocol):

protocol MyCustomCodable {
    static func buildQuery()
}

struct A: MyCustomCodable {
    ...
}

struct B: MyCustomCodable {
    ...
}

...

Тогда моя проблема: у меня где-то есть код, который я применяю к группе из них:

    func updateAllData(customCodableArrays: [[some MyCustomCodable]]) {
        for customCodableArray in customCodableArrays {
            type(of: dbCodablesArray).Element.buildQuery()
        }
    }

    let As: [A] = [A(), A()]
    let Bs: [B] = [] // some arrays can be empty ! That's why buildQuery is static.

    updateAllData([As, Bs])

Но здесь Swift жалуется, что при вызове updateAllData B не может быть преобразован в A.

Я также попробовал что-то вроде этого:

    func updateAllData(customCodableArrays: [some MyCustomCodable]...) {
        for customCodableArray in customCodableArrays {
            type(of: dbCodablesArray).Element.buildQuery()
        }
    }

    let As: [A] = [A(), A()]
    let Bs: [B] = []

    updateAllData([As, Bs])

Но «некоторые», похоже, не разрешены в вариациях.

Я пробовал с любым (или без него, поскольку это неявно):

    func updateAllData(customCodableArrays: [[any MyCustomCodable]]) {
        for customCodableArray in customCodableArrays {
            type(of: dbCodablesArray).Element.buildQuery()
        }
    }

    let As: [A] = [A(), A()]
    let Bs: [B] = []

    updateAllData([As, Bs])

Но здесь вызов updateAllData завершается неудачей, поскольку «статический член updateAllData не может использоваться в метатипе протокола (любой MyCustomCodable).Type»

Единственное решение, которое я нашел, — это передать массивы через отдельные аргументы в updateAllData с некоторым ключевым словом. Но это недопустимое решение, поскольку я не знаю, сколько таблиц мне понадобится, и это заставляет меня дублировать (более или менее) код для каждой из них (я не могу просто перебирать все массивы).

... Я не вижу, как решить мою проблему... Пожалуйста, помогите!

Я пробовал использовать Any, Some, Variadic, Any или casting.

Имеет ли значение содержимое массивов? Как насчет просто updateAllData(A.self, B.self)? Что делает buildQuery? Разве он не возвращает значение и имеет только побочные эффекты? Чтобы решить реальную проблему, вам понадобится пакет параметров, но я все равно задаю эти вопросы, потому что вы, возможно, идете в неправильном направлении.

Sweeper 16.06.2024 14:46
buildQuery реализован в расширении MyCustomCodable и строит SQL-запрос на основе полей структуры. Содержимое массива не имеет значения (просто это то, что у меня есть в качестве исходных входных данных). Я пытался передать тип вместо экземпляра массива (извлекая его из массива), но это систематически вызывает ту же проблему, что и полученный тип any, и блокирует использование buildQuery. Если я приведу отдельные аргументы, как вы предлагаете, это сработает. Но, как уже упоминалось, я не знаю, сколько аргументов мне понадобится...
Guillaume PELOUAS 16.06.2024 15:37

На самом деле я мог бы упростить вопрос о том, как вызвать статический метод протокола, имея тип любого протокола... Я понимаю, что означает любой, но не понимаю, почему это предполагает столь сильные ограничения в языке.

Guillaume PELOUAS 16.06.2024 15:48

Значит, buildQuery вернет один и тот же результат для каждого экземпляра A, поскольку это статический метод?

Joakim Danielson 16.06.2024 15:51

Итак, buildQuery действительно что-то возвращает? И я предполагаю, что в updateAllData вы хотите получить все, что возвращают все buildQuery, и поместить их в массив или что-то в этом роде?

Sweeper 16.06.2024 15:54

buildQuery создаем запрос, смешивающий данные из всех структур (невозможно сделать отдельно), и запускаем его. Он ничего не возвращает.

Guillaume PELOUAS 16.06.2024 16:21

Может быть, добавить пример, потому что я не понимаю, что он делает? «смешивание данных», что это значит, к каким данным вы получаете доступ из статического метода?

Joakim Danielson 16.06.2024 16:33

Вы правы, я путаю вещи, настоящий buildQuery возвращает элементы для построения запроса, касающиеся имени поля и типа типа структуры. Затем updateAll обрабатывает их вместе, чтобы создать и выполнить глобальный запрос. ... но я не думаю, что это имеет отношение к текущей проблеме. Проблема, с которой я столкнулся, - это то, с чем я сталкивался много раз, поскольку использую протоколы (не специфичные для этого варианта использования), и где я так и не нашел удовлетворительного решения.

Guillaume PELOUAS 16.06.2024 17:52
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
8
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ваша фундаментальная проблема заключается в том, что [[some MyCustomCodable]] определяет двумерный массив, в котором все элементы должны быть одного типа (что соответствует MyCustomCodable). Вот почему нельзя смешивать массивы типа [A] и [B]. Альтернатива [[any MyCustomCodable]] определяет двумерный массив, любой элемент которого может иметь любой тип, соответствующий протоколу. Это бесполезно, потому что выражение type(of: MyCustomCodable).Element.buildQuery() должно выбирать конкретную реализацию статической функции, а оно не может этого сделать, поскольку все элементы могут иметь разные соответствующие типы.

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

Ваш протокол остается прежним, но вы определяете другой протокол для коллекций MyCustomCodable, который соответствует Collection, а элементы должны быть ограничены в соответствии с MyCustomCodable. то есть

protocol MyCustomCodable {
    static func buildQuery()
}

protocol MyCustomCodableCollection: Collection 
where Element: MyCustomCodable 
{
    func buildQuery()
}

У него есть функция-член под названием buildQuery()

Затем вы заставляете Array соответствовать MyCustomCodableCollection, что на самом деле очень просто, потому что Array уже является Collection. Вы можете реализовать это следующим образом:

extension Array: MyCustomCodableCollection where Element: MyCustomCodable
{
    func buildQuery() 
    {
        Element.buildQuery()
    }
}

Тогда ваша функция обновления будет выглядеть так:

func updateAllData(customCodableArrays: [any MyCustomCodableCollection]) {
    for customCodableArray in customCodableArrays {
        customCodableArray.buildQuery()
    }
}

Причина, по которой я говорю, что у него есть другие преимущества, заключается в том, что (в зависимости от того, что должен делать buildQuery()) вы можете переместить этот код в функции static в код функции соответствующего типа массива или использовать общие функции или какой-либо другой трюк, чтобы уберите функцию static для каждого типа.

Хо! Отличное решение!!! Я попробую этот!

Guillaume PELOUAS 16.06.2024 20:03

Я работаю! Ранее я пытался напрямую расширить Array следующим образом: extension Array where Element: MyCustomCodable { func buildQuery() -> QueryStructure { Element.buildQuery() } } Но при попытке вызвать его возникла та же проблема... Теперь я понимаю, в чем была проблема. Большое спасибо

Guillaume PELOUAS 16.06.2024 20:18

@GuillaumePELOUAS Также посмотрите Ответ Sweeper. Пакеты параметров появились сравнительно недавно и могут оказаться лучшим решением в зависимости от вашего варианта использования.

JeremyP 17.06.2024 15:46
Ответ принят как подходящий

Для этого вы также можете использовать пакеты параметров.

func updateAllData<each T: MyCustomCodable>(customCodableArrays: repeat [each T]) {
    repeat type(of: each customCodableArrays).Element.buildQuery()
}
updateAllData(customCodableArrays: As, Bs)

или, если содержимое массивов не имеет значения, возьмите метатипы напрямую:

func updateAllData<each T: MyCustomCodable>(types: repeat (each T).Type) {
    repeat (each types).buildQuery()
}
updateAllData(types: A.self, B.self)

Если buildQuery возвращает что-то, скажем, Query, вы можете собрать их все в массив следующим образом:

func updateAllData<each T: MyCustomCodable>(types: repeat (each T).Type) {
    var queries = [Query]()
    repeat array.append((each types).buildQuery())
}

Привет, я не знал этого синтаксиса! Это довольно сложно, но решение не предполагает добавления протокола и расширения массива. Спасибо !

Guillaume PELOUAS 17.06.2024 19:30

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