【问题标题】:Get the name (string) of a generic type in Swift在 Swift 中获取泛型类型的名称(字符串)
【发布时间】:2014-10-18 06:05:26
【问题描述】:

我有一个 T 类型的泛型类,我想获取实例化时传递给该类的类型的名称。这是一个例子。

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

我已经四处寻找了几个小时,但似乎找不到任何方法来做到这一点。有人试过吗?

非常感谢任何帮助。

谢谢

【问题讨论】:

  • 这取决于您所说的“名称”。通过打印类对象,您可以很容易地根据 Objective-C 获得名称。但是,对于在 Swift 中定义的类,这个名称可能会被破坏。如果您只需要了解它是什么,那就足够了。

标签: generics types swift introspection


【解决方案1】:

为我工作:

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

【讨论】:

    【解决方案2】:

    String(describing: T.self) 在 Swift 3+ 中

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

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

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

    包括类和结构的示例

    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)
    

    控制台输出

    /*
    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'
    */
    

    【讨论】:

      【解决方案3】:

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

      一种可能的解决方法是:

      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
              }
          }
      }
      

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

      如果这是泛型类型:

      class TestClass {}
      

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

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

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

      测试如下:

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

      更新 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
      }
      

      【讨论】:

      • 感谢您的回复。我试过你的想法。不幸的是,我将在泛型中设置的大多数类型都不符合 AnyObject。 T 也有可能是一个协议。
      • 在这种情况下,恐怕您必须等待 swift 提供更好的内省支持。我很难实现一个简单的事情,比如使用纯 swift 获取name of a protocol at runtime
      【解决方案4】:

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

      游乐场

      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()
      

      【讨论】:

        【解决方案5】:

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

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

        您可以在操场上试用它,它可以按预期工作:

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

        【讨论】:

        • 谢谢!你也可以写String(T.self)。
        • 这似乎是提取类型名称的最佳方式。又短又甜!
        • 不错!非常有用。
        【解决方案6】:

        如果你的类型参数实现了一个通用的命名协议,这是可能的。

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

        请注意,这适用于类和值类型,因为后者也可以扩展以符合协议,如下面的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
        

        【讨论】:

        • 这是一个不错的技巧,但你应该提到它非常有限 - 例如泛型类型没有泛型参数,不能为内部类型提供公共扩展,不能混合和匹配访问说明符,不能以这种方式扩展协议,等等。
        猜你喜欢
        • 2017-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多