El siguiente código (compila sin errores) recupera el índice de un elemento en un tipo de enumeración CaseIterable particular
public enum MyEnum : CaseIterable { case ONE, TWO, THREE public func ordinal() -> Int? { return MyEnum.allCases.firstIndex(of: self) } }Quiero hacer una función genérica para trabajar con todas las enumeraciones CaseIterable.
Si intento:
public extension CaseIterable { public func ordinal() -> Int? { return CaseIterable.allCases.firstIndex(of: self) } } Recibo un error del compilador " Member 'allCases' cannot be used on value of protocol type 'CaseIterable'; use a generic constraint instead " que es bastante lógico, ya que se desconoce el tipo de enumeración real.
Cuando intento CaseIterable<T> , aparece otro error, ya que CaseIterable no se declara como tipo genérico.
¿Hay alguna manera?
Un par de cambios son necesarios:
Self.AllCases.Index? en lugar de Int? . En la práctica, estos tipos serán equivalentes, como se verá a continuación.Equatable , porque debe ser equatable para usar firstIndex(of:) . Nuevamente, en la práctica, cualquier CaseIterable generalmente será una enumeración sin valores asociados, lo que significa que será equiparable automáticamente.nil , porque está encontrando un caso en un CaseIterable . Por lo tanto, puede eliminar la opcionalidad en el tipo de retorno ( Self.AllCases.Index ) y forzar el desenvolvimiento.Ejemplo:
public extension CaseIterable where Self: Equatable { public func ordinal() -> Self.AllCases.Index { return Self.allCases.firstIndex(of: self)! } } enum Example: CaseIterable { case x case y } Example.y.ordinal() // 1 type(of: Example.y.ordinal()) // Int Personalmente, agregaría que "Ordinal" generalmente significa algo diferente de lo que está haciendo, y recomendaría cambiar el nombre de la función a elementIndex() o algo así. Pero eso es un aparte.