#swift #generics #delegates #protocols
#Swift #Общие #Делегаты #Протоколы
Вопрос:
Я пытаюсь заставить функцию принимать общий ввод, соответствующий как классу, так и протоколу, чтобы обрабатывать значения, в которых некоторые принадлежат классу, а другие принадлежат делегату. Он отлично работает для отдельных типов, но не для массива типов (которые соответствуют как суперклассу, так и делегату). Вместо этого компилятор выдает ошибку «Метод экземпляра ‘printValues(for:)’ требует, чтобы ‘MeasurementClass’ соответствовал ‘UnitDelegate'»
Я добавил приведенный ниже код, который будет иметь гораздо больше смысла. Просто поместите его на игровую площадку Swift. Если вы запустите его как есть, вы увидите, что он правильно выводит расстояние и шаги пользователя по отдельности. Если вы раскомментируете последние несколько строк, ту же функцию нельзя будет использовать с массивом.
Моя цель — иметь возможность передавать массив типов, которые соответствуют UnitDelegate и MeasurementClass (он же MeasurementObject typealias) и обрабатывать значения. Мне нужен массив, потому что мне понадобится иметь кучу разных классов, которые соответствуют MeasurementObject.
protocol UnitDelegate: class{
var units: [String: String] { get }
}
class MeasurementClass{
var imperial: Double!
var metric: Double!
convenience init(imperial: Double, metric: Double){
self.init()
self.imperial = imperial
self.metric = metric
}
}
typealias MeasurementObject = MeasurementClass amp; UnitDelegate
class Distance: MeasurementObject{
var units = ["imperial": "miles", "metric":"kms"]
}
class Steps: MeasurementObject{
var units = ["imperial": "steps", "metric":"steps"]
}
class User{
func printValues<T: MeasurementObject>(for type: T) {
print("(type.imperial!) (type.units["imperial"]!) = (type.metric!) (type.units["metric"]!)")
}
}
//This is what I'm trying to achieve in the for loop below
let distance = Distance(imperial: 30, metric: 48.28)
let steps = Steps(imperial: 30, metric: 30)
let user = User()
user.printValues(for: distance)
user.printValues(for: steps)
//let types = [distance, steps]
//
//for type in types{
// user.printValues(for: type)
//}
Комментарии:
1. Это не так, как работает generic. Вы не можете смешивать общие типы в коллекции. Универсальный тип может быть любого типа, но только по одному за раз
2. ` var imperial: удваивается! Метрика var: двойная! ` Я гарантирую вам, что эти два значения не будут синхронизированы. Сохраните только один источник достоверности, например, стандартное значение метрики, и сделайте имперское измерение вычисляемым свойством, которое получает правильное преобразование на лету. Таким образом, он никогда не может выйти из синхронизации
3. @Alexander-RestorateMonica Я подумал об этом и решил, что это лучшее. Я храню кучу типов работоспособности на сервере, и, сохраняя imperial и metric, я могу просто извлечь правильное значение. Если бы у меня был только imperial, мне пришлось бы каким-то образом переключить тип данных, которые извлекаются для выполнения преобразования. Имея все записи работоспособности, хранящиеся с одинаковыми значениями на сервере, на стороне собственного приложения я могу использовать один класс для декодирования каждого типа.
4. @RichardWitherspoon я предлагаю вам использовать одно значение как единственное, которое стоит сохранить. Взгляните на API единиц измерения и измерений , встроенный Apple в Foundation. Из того немногого, что я видел о вашем коде / проблеме, это выглядит как точное соответствие тому, что вы пытаетесь сделать. Если нет, вы могли бы хотя бы сослаться на него, например, на их базовые единицы: developer.apple.com/documentation/foundation/dimension
5. Я понятия не имел об этом, это будет идеально! Большое вам спасибо.
Ответ №1:
Это не прямой ответ на ваш вопрос, но все, что вам нужно, это добавить перечисление для хранения типа измерения в вашем классе и вычисляемое свойство для возврата соответствующего словаря. Нет необходимости использовать протокол, состав протокола, класс и / или подкласс для выполнения того, что вы пытаетесь сделать:
struct AMeasurement {
let imperial: Double
let metric: Double
let kind: Kind
enum Kind { case distance, steps }
var units: [String: String] {
switch kind {
case .distance: return ["imperial": "miles", "metric":"kms"]
case .steps: return ["imperial": "steps", "metric":"steps"]
}
}
}
extension AMeasurement {
func printValues() {
print("(imperial) (units["imperial"]!) = (metric) (units["metric"]!)")
}
}
let distance = AMeasurement(imperial: 30, metric: 48.28, kind: .distance)
let steps = AMeasurement(imperial: 30, metric: 30, kind: .steps)
let types = [distance, steps]
for type in types {
type.printValues()
}
30,0 миль = 48,28 км
30,0 шагов = 30,0 шагов
Комментарии:
1. Это гораздо более простой подход к решению моей проблемы. Кажется, я слишком усложнил ответ. Спасибо за помощь!