В качестве дополнения to my answer here, вы можете достичь этого статически, используя «теневой метод», чтобы позволить типам Numeric
принуждать себя к любому другому типу Numeric
(учитывая, что инициализатор для типа назначения указан как требование протокола).
Например, вы можете определить свой протокол Numeric
так:
protocol Numeric : Comparable, Equatable {
init(_ v:Float)
init(_ v:Double)
init(_ v:Int)
init(_ v:UInt)
init(_ v:Int8)
init(_ v:UInt8)
init(_ v:Int16)
init(_ v:UInt16)
init(_ v:Int32)
init(_ v:UInt32)
init(_ v:Int64)
init(_ v:UInt64)
init(_ v:CGFloat)
// 'shadow method' that allows instances of Numeric
// to coerce themselves to another Numeric type
func _asOther<T:Numeric>() -> T
}
extension Numeric {
// Default implementation of init(fromNumeric:) simply gets the inputted value
// to coerce itself to the same type as the initialiser is called on
// (the generic parameter T in _asOther() is inferred to be the same type as self)
init<T:Numeric>(fromNumeric numeric: T) { self = numeric._asOther() }
}
И тогда соответствуют типы, Numeric
следующим образом: использование
// Implementations of _asOther() – they simply call the given initialisers listed
// in the protocol requirement (it's required for them to be repeated like this,
// as the compiler won't know which initialiser you're referring to otherwise)
extension Float : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Double : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension CGFloat : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int8 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int16 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int32 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int64 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt8 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt16 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt32 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt64 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
Пример:
class MyClass<T : Numeric> {
var top : T
init(_ top:T) {
self.top = top
}
func topAsCGFloat() -> CGFloat {
return CGFloat(fromNumeric: top)
}
}
let m = MyClass(Int32(42))
let converted = m.topAsCGFloat()
print(type(of:converted), converted) // prints: CGFloat 42.0
Это решение, вероятно, не короче, чем реализация метода , который переключается через каждый тип, который соответствует Numeric
. Однако, поскольку это решение не зависит от типа выполнения, компилятор, скорее всего, получит больше возможностей для оптимизации.
Он также выгоды от статического проверки типа, что означает, что вы не можете соответствовать новому типу к Numeric
без также реализует логику для преобразования этого типа к другому типу Numeric
(в вашем случае, вы бы врезаться во время выполнения, если типа не обрабатывался в switch
).
Кроме того, из-за инкапсуляции более гибко расширяться, поскольку логика для преобразования типов выполняется в каждом конкретном конкретном типе, который соответствует Numeric
, а не одному методу, который обрабатывает возможные случаи.
Связанный: [Как преобразовать между связанными типами через общий инициализатор?] (Http://stackoverflow.com/questions/37556276/how-can-i-convert-between-related-types-through-a- общего инициализатор). Вы можете решить свою проблему статически, добавив «теневой метод» к вашему требованию к протоколу, например '_asOther() -> T', позволяя каждому типу, который соответствует протоколу, преобразовать себя в другой тип. – Hamish
Не могли бы вы показать реализацию на основе моего примера? Я был бы рад принять этот ответ, если он будет чище/короче и т. Д. –
Я не уверен, считаете ли вы его более чистым или коротким, но есть преимущества в этом, как я предложил, что я объяснено в моем ответе ниже :) – Hamish