获取Swift中泛型类型的名称(字符串)

时间:2021-01-07 23:37:58

I have a generic class of type T and I would like to get the name of the type that passed into the class when instantiated. Here is an example.

我有一个类型为T的泛型类,我希望获得在实例化时传递给类的类型的名称。这是一个例子。

class MyClass<T> {
    func genericName() -> String {
        // Return the name of T.
    }
}

I have been looking around for hours and I can't seem to find any way to do this. Has anyone tried this yet?

我一直在寻找几个小时,我似乎无法找到任何方法来做到这一点。有人试过这个吗?

Any help is greatly appreciated.

任何帮助是极大的赞赏。

Thanks

谢谢

5 个解决方案

#1


8  

A pure swift way to achieve that is not possible.

实现这一目标的纯粹快捷方式是不可能的。

A possible workaround is:

可能的解决方法是:

class MyClass<T: AnyObject> {
    func genericName() -> String {
        let fullName: String = NSStringFromClass(T.self)
        let range = fullName.rangeOfString(".", options: .BackwardsSearch)
        if let range = range {
            return fullName.substringFromIndex(range.endIndex)
        } else {
            return fullName
        }
    }
}

The limitations relies on the fact that it works with classes only.

限制依赖于它仅适用于类的事实。

If this is the generic type:

如果这是泛型类型:

class TestClass {}

NSStringFromClass() returns the full name (including namespace):

NSStringFromClass()返回全名(包括命名空间):

// Prints something like "__lldb_expr_186.TestClass" in playground
NSStringFromClass(TestClass.self)

That's why the func searches for the last occurrence of the . character.

这就是func搜索最后一次出现的原因。字符。

Tested as follows:

测试如下:

var x = MyClass<TestClass>()
x.genericName() // Prints "TestClass"

UPDATE Swift 3.0

更新Swift 3.0

func genericName() -> String {
    let fullName: String = NSStringFromClass(T.self)
    let range = fullName.range(of: ".")
    if let range = range {
        return fullName.substring(from: range.upperBound)
    }
    return fullName
}

#2


14  

You can return any types' name by using string interpolation:

您可以使用字符串插值返回任何类型的名称:

class MyClass<T> {
    func genericName() -> String {
        return "\(T.self)"
    }
}

You can try it in a playground and it works as expected:

你可以在游乐场尝试它,它按预期工作:

var someClass = MyClass<String>()
someClass.genericName() // Returns "Swift.String"

#3


8  

String(describing: T.self) in Swift 3+

var genericTypeName: String {
    return String(describing: T.self)
}

Within the generic type, get the name of type T by converting T.self or type(of: T.self) to a String. I found that type(of:) was not necessary but it's worth being aware of since in other cases it removes other details about the Type.

在泛型类型中,通过将T.self或type(of:T.self)转换为String来获取类型T的名称。我发现类型(of :)不是必需的,但值得注意,因为在其他情况下它会删除有关Type的其他细节。

The following example demonstrates getting the name of the generic type T within a struct and a class. It includes code to get the name of the containing type.

以下示例演示如何在结构和类中获取泛型类型T的名称。它包含获取包含类型名称的代码。

Example including class and struct

struct GenericStruct<T> {
    var value: T

    var genericTypeName: String {
        return String(describing: T.self)
    }

    var genericTypeDescription: String {
        return "Generic Type T: '\(genericTypeName)'"
    }

    var typeDescription: String {
        // type(of:) is necessary here to exclude the struct's properties from the string
        return "Type: '\(type(of: self))'"
    }
}

class GenericClass<T> {
    var value: T

    var genericTypeName: String {
        return String(describing: T.self)
    }

    var genericTypeDescription: String {
        return "Generic Type T: '\(genericTypeName)'"
    }

    var typeDescription: String {
        let typeName = String(describing: self)
        return "Type: '\(typeName)'"
    }

    init(value: T) {
        self.value = value
    }
}

enum TestEnum {
    case value1
    case value2
    case value3
}

let intGenericStruct: GenericStruct<Int> = GenericStruct(value: 1)
print(intGenericStruct.typeDescription)
print(intGenericStruct.genericTypeDescription)

let enumGenericStruct: GenericStruct<TestEnum> = GenericStruct(value: .value2)
print(enumGenericStruct.typeDescription)
print(enumGenericStruct.genericTypeDescription)

let intGenericClass: GenericClass<Int> = GenericClass(value: 1)
print(intGenericClass.typeDescription)
print(intGenericClass.genericTypeDescription)

let enumGenericClass: GenericClass<TestEnum> = GenericClass(value: .value2)
print(enumGenericClass.typeDescription)
print(enumGenericClass.genericTypeDescription)

Console Output

/*
Type: 'GenericStruct<Int>'
Generic Type T: 'Int'

Type: 'GenericStruct<TestEnum>'
Generic Type T: 'TestEnum'

Type: 'GenericClass<Swift.Int>'
Generic Type T: 'Int'

Type: 'GenericClass<TestEnum>'
Generic Type T: 'TestEnum'
*/

#4


1  

It's possible if your type parameter implements a common naming protocol.

如果您的类型参数实现了通用的命名协议,那么这是可能的。

In the example below the protocol Named ensures that the generic type implements the name class property.

在下面的示例中,协议Named确保泛型类型实现名称类属性。

Note that this works with both classes and value types since the latter can also be extended to conform to protocols, as illustrated with the Int below.

请注意,这适用于类和值类型,因为后者也可以扩展为符合协议,如下面的Int所示。

protocol Named {
    class var name: String { get }
}

class MyClass<T: Named> {
    func genericName() -> String {
        return T.name
    }
}

extension Int: Named {
    static var name: String { return "I am an Int" }
}

class Foo: Named {
    class var name: String { return "I am a Foo" }
}

enum Drink: Named {
    static var name: String { return "I am a Drink" }
}

MyClass<Int>().genericName()  // I am an Int
MyClass<Foo>().genericName()  // I am a Foo
MyClass<Drink>().genericName()  // I am a Drink

#5


0  

Another possible solution that might help somebody:

另一种可能对某人有帮助的解决方案:

Playground

操场

import Foundation

class ClassType<T> {

    static func name () -> String
    {
        return "\(T.self)".componentsSeparatedByString(".").last!
    }
}

class MyClass {

}

func testClassName(){

    let className = ClassType<MyClass>.name()
    print(className)
}

testClassName()

#1


8  

A pure swift way to achieve that is not possible.

实现这一目标的纯粹快捷方式是不可能的。

A possible workaround is:

可能的解决方法是:

class MyClass<T: AnyObject> {
    func genericName() -> String {
        let fullName: String = NSStringFromClass(T.self)
        let range = fullName.rangeOfString(".", options: .BackwardsSearch)
        if let range = range {
            return fullName.substringFromIndex(range.endIndex)
        } else {
            return fullName
        }
    }
}

The limitations relies on the fact that it works with classes only.

限制依赖于它仅适用于类的事实。

If this is the generic type:

如果这是泛型类型:

class TestClass {}

NSStringFromClass() returns the full name (including namespace):

NSStringFromClass()返回全名(包括命名空间):

// Prints something like "__lldb_expr_186.TestClass" in playground
NSStringFromClass(TestClass.self)

That's why the func searches for the last occurrence of the . character.

这就是func搜索最后一次出现的原因。字符。

Tested as follows:

测试如下:

var x = MyClass<TestClass>()
x.genericName() // Prints "TestClass"

UPDATE Swift 3.0

更新Swift 3.0

func genericName() -> String {
    let fullName: String = NSStringFromClass(T.self)
    let range = fullName.range(of: ".")
    if let range = range {
        return fullName.substring(from: range.upperBound)
    }
    return fullName
}

#2


14  

You can return any types' name by using string interpolation:

您可以使用字符串插值返回任何类型的名称:

class MyClass<T> {
    func genericName() -> String {
        return "\(T.self)"
    }
}

You can try it in a playground and it works as expected:

你可以在游乐场尝试它,它按预期工作:

var someClass = MyClass<String>()
someClass.genericName() // Returns "Swift.String"

#3


8  

String(describing: T.self) in Swift 3+

var genericTypeName: String {
    return String(describing: T.self)
}

Within the generic type, get the name of type T by converting T.self or type(of: T.self) to a String. I found that type(of:) was not necessary but it's worth being aware of since in other cases it removes other details about the Type.

在泛型类型中,通过将T.self或type(of:T.self)转换为String来获取类型T的名称。我发现类型(of :)不是必需的,但值得注意,因为在其他情况下它会删除有关Type的其他细节。

The following example demonstrates getting the name of the generic type T within a struct and a class. It includes code to get the name of the containing type.

以下示例演示如何在结构和类中获取泛型类型T的名称。它包含获取包含类型名称的代码。

Example including class and struct

struct GenericStruct<T> {
    var value: T

    var genericTypeName: String {
        return String(describing: T.self)
    }

    var genericTypeDescription: String {
        return "Generic Type T: '\(genericTypeName)'"
    }

    var typeDescription: String {
        // type(of:) is necessary here to exclude the struct's properties from the string
        return "Type: '\(type(of: self))'"
    }
}

class GenericClass<T> {
    var value: T

    var genericTypeName: String {
        return String(describing: T.self)
    }

    var genericTypeDescription: String {
        return "Generic Type T: '\(genericTypeName)'"
    }

    var typeDescription: String {
        let typeName = String(describing: self)
        return "Type: '\(typeName)'"
    }

    init(value: T) {
        self.value = value
    }
}

enum TestEnum {
    case value1
    case value2
    case value3
}

let intGenericStruct: GenericStruct<Int> = GenericStruct(value: 1)
print(intGenericStruct.typeDescription)
print(intGenericStruct.genericTypeDescription)

let enumGenericStruct: GenericStruct<TestEnum> = GenericStruct(value: .value2)
print(enumGenericStruct.typeDescription)
print(enumGenericStruct.genericTypeDescription)

let intGenericClass: GenericClass<Int> = GenericClass(value: 1)
print(intGenericClass.typeDescription)
print(intGenericClass.genericTypeDescription)

let enumGenericClass: GenericClass<TestEnum> = GenericClass(value: .value2)
print(enumGenericClass.typeDescription)
print(enumGenericClass.genericTypeDescription)

Console Output

/*
Type: 'GenericStruct<Int>'
Generic Type T: 'Int'

Type: 'GenericStruct<TestEnum>'
Generic Type T: 'TestEnum'

Type: 'GenericClass<Swift.Int>'
Generic Type T: 'Int'

Type: 'GenericClass<TestEnum>'
Generic Type T: 'TestEnum'
*/

#4


1  

It's possible if your type parameter implements a common naming protocol.

如果您的类型参数实现了通用的命名协议,那么这是可能的。

In the example below the protocol Named ensures that the generic type implements the name class property.

在下面的示例中,协议Named确保泛型类型实现名称类属性。

Note that this works with both classes and value types since the latter can also be extended to conform to protocols, as illustrated with the Int below.

请注意,这适用于类和值类型,因为后者也可以扩展为符合协议,如下面的Int所示。

protocol Named {
    class var name: String { get }
}

class MyClass<T: Named> {
    func genericName() -> String {
        return T.name
    }
}

extension Int: Named {
    static var name: String { return "I am an Int" }
}

class Foo: Named {
    class var name: String { return "I am a Foo" }
}

enum Drink: Named {
    static var name: String { return "I am a Drink" }
}

MyClass<Int>().genericName()  // I am an Int
MyClass<Foo>().genericName()  // I am a Foo
MyClass<Drink>().genericName()  // I am a Drink

#5


0  

Another possible solution that might help somebody:

另一种可能对某人有帮助的解决方案:

Playground

操场

import Foundation

class ClassType<T> {

    static func name () -> String
    {
        return "\(T.self)".componentsSeparatedByString(".").last!
    }
}

class MyClass {

}

func testClassName(){

    let className = ClassType<MyClass>.name()
    print(className)
}

testClassName()