如何根据通用类型在Swift中具有默认和专用功能?

如何根据通用类型在Swift中具有默认和专用功能?,swift,generics,Swift,Generics,我有一个通用的结构,它接受任何类型,还有一个函数: struct Test<T> { func makeSomething() -> T? { print("Default implementation") return nil } } 如果T符合TestProtocol,我想让makeSomething函数变得专业化。用法示例: struct TestItem: TestProtocol { static func d

我有一个通用的
结构
,它接受任何类型,还有一个函数:

struct Test<T> {
    func makeSomething() -> T? {
        print("Default implementation")
        return nil
    }
}
如果
T
符合
TestProtocol
,我想让
makeSomething
函数变得专业化。用法示例:

struct TestItem: TestProtocol {
    static func defaultValue() -> TestItem {
        return TestItem()
    }
}

let normalTest: Test<String> = Test()
let normalReturn = normalTest.makeSomething() // Should use default, returns nil.

let specialTest: Test<TestItem> = Test()
let specialReturn = specialTest.makeSomething() // Should use specialised, returns `TestItem`.
问题:尝试使用
makeSomething
函数会导致错误:
对“makeSomething()”的使用不明确。
。我理解错误发生的原因;仅仅因为我创建了一个专门的函数,并不意味着默认函数的有效性更低,因此它不知道使用哪个函数

也可以将默认实现移动到扩展中,但它必须具有类型约束,这些约束表示“任何不符合
TestProtocol
”的类型,据我所知,这是不可能的

选择2 向
makeSomething
功能添加专门的部件,使其根据
T
的类型改变其行为:

func makeSomething() -> T? {
    if let specialType = T.self as? TestProtocol.Type {
        print("Special implementation")
        return specialType.defaultValue()
    } else {
        print("Default implementation")
        return nil
    }
}
问题:正如预期的那样,这不起作用,因为
specialType
现在是
TestProtocol.type
而不是
t
,所以我得到错误:
无法将类型为“TestProtocol”的返回表达式转换为返回类型“t”
。在这种情况下,我不知道如何让编译器知道
t
TestProtocol



我遇到的实际问题更复杂,但我认为这简化了它,并正确地说明了我遇到的问题。是否有一种方法可以根据
T
在上述约束范围内的一致性来实现默认和专门的功能?

最简单的解决方案是简单地使用选项2,然后将结果转换回
T
,以弥补我们不能将
T.self
转换为抽象类型这一事实的差距属于同样符合测试协议的
T.self
。类型:

func makeSomething() -> T? {
    if let specialType = T.self as? TestProtocol.Type {
        print("Special implementation")
        return specialType.defaultValue() as? T
    } else {
        print("Default implementation")
        return nil
    }
}
由于
defaultValue
返回
Self
,此转换应该永远不会失败(我们使用的是条件转换,因为该方法返回可选值)

尽管如此,依赖运行时类型转换并不是解决这个问题的特别好的方法。实现您在选项1中试图实现的重载的一种可能方式是使用协议–当给定的关联类型
某物
符合
TestProtocol
时,允许静态调度和重载

// feel free to give this a better name
protocol MakesSomething {
    associatedtype Something
    func makeSomething() -> Something?
}

// default implementation
extension MakesSomething {
    func makeSomething() -> Something? {
        return nil
    }
}

protocol TestProtocol {
    static func defaultValue() -> Self
}

// specialised implementation
extension MakesSomething where Something : TestProtocol {
    func makeSomething() -> Something? { 
        return Something.defaultValue()
    }
}

struct Test<T> : MakesSomething {
    // define T == Something
    typealias Something = T
}
//请随意给它起个更好的名字
协议生成方法{
关联类型
func makeSomething()->Something?
}
//默认实现
扩展方法{
func makeSomething()->Something{
归零
}
}
协议测试协议{
静态func defaultValue()->Self
}
//专门实施
扩展makesMething,其中包含:TestProtocol{
func makeSomething()->Something?{
返回一些内容。defaultValue()
}
}
结构测试:makesMething{
//定义T==某物
typealias某物=T
}

let normalTest:Test=Test()
让normalReturn=normalTest.makeSomething()//nil
让specialTest:Test=Test()
让specialReturn=specialTest.makeSomething()//TestItem()

也许不是最方便的解决方案,因为它涉及到创建一个新类型——但据我所知,实现您所追求的条件重载行为的唯一方法是通过协议。尽管这取决于您试图解决的实际问题,但您可能能够将其集成到现有的协议中。

如果您有多个不同的测试协议,那么在makeSomething()方法中对所有协议都具有依赖性可能会变得不方便

使用两个级别的协议有助于保持测试关注点与各种测试协议的分离

 protocol TestSpecialization
 {
    static func makeSomething()->Self?
 }

 class Test<T> 
 {

    init(_ type:T.Type) {}    // the init is merely for syntax candy

    typealias TestedType = T

    func makeSomething() -> TestedType?  
    { 
      // only one exception here rather than one for every test protocol
      if TestedType.self is TestSpecialization.Type 
      {
        return (TestedType.self as! TestSpecialization.Type).makeSomething() as? TestedType
      }
      print("default implementation")
      return nil 
    }
 }


 // all your test protocols should conform to TestSpecialization
 // using a default implementation for makeSomething()
 //
 protocol TestProtocol:TestSpecialization 
 {
     static func defaultValue() -> Self
 }

 extension TestProtocol
 {
     // this is extra work but it keeps the "special" out of the Test<T> class
     static func makeSomething()->Self?
     {
        print("Special implementation")
        return defaultValue()
     }
 }

 // everything else works as expected

 struct TestItem: TestProtocol 
 {
     static func defaultValue() -> TestItem 
     {
         return TestItem()
     }
 }

 let normalTest   = Test(String)
 let normalReturn = normalTest.makeSomething() // uses default, returns nil.

 let specialTest   = Test(TestItem)
 let specialReturn = specialTest.makeSomething() // uses specialised, returns `TestItem`.
协议测试专用化
{
静态函数makeSomething()->Self?
}
课堂测试
{
init({type:T.type){}//init仅用于语法分析
typealias TestedType=T
func makeSomething()->TestedType?
{ 
//这里只有一个异常,而不是每个测试协议都有一个异常
如果TestedType.self是TestSpecialization.Type
{
return(TestedType.self为!TestSpecialization.Type).makeSomething()为?TestedType
}
打印(“默认实现”)
归零
}
}
//您的所有测试协议都应该符合TestSpecialization
//使用makeSomething()的默认实现
//
协议TestProtocol:TestSpecialization
{
静态func defaultValue()->Self
}
扩展测试协议
{
//这是额外的工作,但它将“特殊”排除在测试类之外
静态函数makeSomething()->Self?
{
打印(“特殊实施”)
返回defaultValue()
}
}
//其他一切都按预期进行
结构TestItem:TestProtocol
{
静态函数defaultValue()->TestItem
{
返回测试项()
}
}
设normalTest=Test(字符串)
让normalReturn=normalTest.makeSomething()//使用默认值,返回nil。
让specialTest=测试(测试项目)
让specialReturn=specialTest.makeSomething()//使用Specialized,返回'TestItem'。

谢谢您的回答!我还想过在选项1中加入演员阵容;它确实管用,但和你一样,我觉得它不太优雅。我想我可能会接受这样一个观点,即通过协议来实现这一点是目前最好的解决方案;也许将来会有更好的方法@罗伯特很乐意帮忙!Swift还相对年轻,发展迅速,因此我相信未来会有更好的解决方案:)
// feel free to give this a better name
protocol MakesSomething {
    associatedtype Something
    func makeSomething() -> Something?
}

// default implementation
extension MakesSomething {
    func makeSomething() -> Something? {
        return nil
    }
}

protocol TestProtocol {
    static func defaultValue() -> Self
}

// specialised implementation
extension MakesSomething where Something : TestProtocol {
    func makeSomething() -> Something? { 
        return Something.defaultValue()
    }
}

struct Test<T> : MakesSomething {
    // define T == Something
    typealias Something = T
}
let normalTest : Test<String> = Test()
let normalReturn = normalTest.makeSomething() // nil

let specialTest : Test<TestItem> = Test()
let specialReturn = specialTest.makeSomething() // TestItem()
 protocol TestSpecialization
 {
    static func makeSomething()->Self?
 }

 class Test<T> 
 {

    init(_ type:T.Type) {}    // the init is merely for syntax candy

    typealias TestedType = T

    func makeSomething() -> TestedType?  
    { 
      // only one exception here rather than one for every test protocol
      if TestedType.self is TestSpecialization.Type 
      {
        return (TestedType.self as! TestSpecialization.Type).makeSomething() as? TestedType
      }
      print("default implementation")
      return nil 
    }
 }


 // all your test protocols should conform to TestSpecialization
 // using a default implementation for makeSomething()
 //
 protocol TestProtocol:TestSpecialization 
 {
     static func defaultValue() -> Self
 }

 extension TestProtocol
 {
     // this is extra work but it keeps the "special" out of the Test<T> class
     static func makeSomething()->Self?
     {
        print("Special implementation")
        return defaultValue()
     }
 }

 // everything else works as expected

 struct TestItem: TestProtocol 
 {
     static func defaultValue() -> TestItem 
     {
         return TestItem()
     }
 }

 let normalTest   = Test(String)
 let normalReturn = normalTest.makeSomething() // uses default, returns nil.

 let specialTest   = Test(TestItem)
 let specialReturn = specialTest.makeSomething() // uses specialised, returns `TestItem`.