Given the method
func enumCaseCount<T: Hashable>(ofType type: T.Type) -> Int {
// Needed check if type is an enum type
return 3
}
Used as follows
private enum SimpleEnum: String {
case a = "A"
case b = "B"
case c = "C"
}
enumCaseCount(ofType: SimpleEnum.self)
Classes can be tested this way
class Test {}
Test.self is AnyClass // returns true
For the fun of it, as a (workaround) hack, we could instantiate an instance of T
and perform runtime instrospection on it using Mirror
, specifically its displayStyle
property. Before we proceed, we note that we'll only use this for debugging purposes
Mirrors are used by playgrounds and the debugger.
I'll also point out that we're really chasing our tail here as we resort to runtime to query things known (by the compiler, at least) at compile time.
Anyway, first of all, I'll rename enumCaseCount(...)
to isEnum(...)
, as this question only covers querying whether a metatype is an enum
or not. For similar (somewhat brittle) hacks to query the number of cases of a given enum
, see:
Now, the generic placeholder T
in isEnum(...)
only knows that it is a type conforming to Hashable
, which doesn't give us any straight-forward way to instantiate an instance of T
(if Hashable
blueprinted, say, an initializer init()
, we'd could readily construct an instance of T
an perform runtime introspection upon it). Instead, we'll resort to manually allocating raw memory for a single T
instance (UnsafeMutableRawPointer.allocate(bytes:alignedTo:)
), binding it to T
(bindMemory(to:capacity:)
), and finally deallocating the memory (deallocate(bytes:alignedTo:)
) once we've finished our runtime introspection of the instance referenced to by the pointer to the bound memory. As for the runtime introspection, we simply use Mirror
to check whether its displayStyle
is enum
or not.
func isEnum<T: Hashable>(_: T.Type) -> Bool {
var result = false
// Allocate memory with size and alignment matching T.
let bytesPointer = UnsafeMutableRawPointer.allocate(
bytes: MemoryLayout<T>.size,
alignedTo: MemoryLayout<T>.alignment)
// Bind memory to T and perform introspection on the instance
// reference to by the bound memory.
if case .some(.`enum`) = Mirror(reflecting:
bytesPointer.bindMemory(to: T.self, capacity: 1).pointee)
.displayStyle {
print("Is an enum")
result = true
} else { print("Is not an enum") }
// Deallocate the manually allocate memory.
bytesPointer.deallocate(bytes: MemoryLayout<T>.size,
alignedTo: MemoryLayout<T>.alignment)
return result
}
Example usage:
enum SimpleEnum { case a, b, c }
enum SimpleStrEnum: String {
case a = "A"
case b = "B"
case c = "C"
}
enum SimpleExplicitIntEnum: Int { case a, b, c }
struct SimpleStruct: Hashable {
let i: Int
// Hashable
var hashValue: Int { return 0 }
static func ==(lhs: SimpleStruct, rhs: SimpleStruct) -> Bool { return true }
}
print(isEnum(SimpleEnum.self)) // true
print(isEnum(SimpleStrEnum.self)) // true
print(isEnum(SimpleExplicitIntEnum.self)) // true
print(isEnum(SimpleStruct.self)) // false