【问题标题】:How should I write this in Swift?我应该如何用 Swift 写这个?
【发布时间】:2017-02-05 18:31:16
【问题描述】:

假设我创建了一个这样的协议:

protocol A {
 associatedtype T
 func computeSomething(with:T) -> Double
}

在我的泛型类型类中,我想做这样的事情:

class B<U> {
 var doSomething:A<U>
}  

这会产生错误,但我愿意接受任何支持我的类型 U 上的 computeSomething 的类型,但我完全不知道该怎么做?

为清楚起见进行编辑

基本上,如果 A 是通用结构或类,那将是可能的,但是如果没有默认实现(由类或结构提供)在这里有意义,我唯一想要的就是确保该类型执行我想要的?

编辑#2(具体示例)

我想简化我的问题,这使得它很难理解,所以这里是一个仍然简化和虚构的问题,可能更符合我遇到的问题:

我正在写一个泛型类来处理它的泛型类型T

class Process<T> { ... }

Process 类本身包含处理T 的代码,但为了使此代码工作,它需要T 符合某些协议,例如:

protocol A {
    func mixWith(other:A) -> A
}

protocol B {
    var isFoo:Bool { get set }
}

所以我的第一个方法是简单地要求 T 符合这些协议:

class Process<T:<A,B>> { ... }

这看起来是最简单的方法,可能在很多情况下都是如此,但就我而言,我认为这实际上是有问题的,原因如下:

首先,我可能需要以多种不同的方式处理同一个类型,并且更改处理类型的方式通常需要更改协议AB 的实际实现,例如在本例中为@987654335 @ 和 barProcess 都是 Process 类型和泛型类型 MyType:

let fooProcess = Process<MyType>()
let barProcess = Process<MyType>() 

但我希望 fooProcessbarProcess 执行不同的操作,这在许多情况下需要更改我的 MyType 类型的 AB 协议的实现,而这根本不可能。

所以我的第一个想法是简单地要求定义一些闭包和变量,这样我就不再需要协议,并且只在我的 Process 类中定义处理数据的方式,有点像这样:

class Process<T> {
//
var mix:(_ lhs:T, _ rhs:T)->(T)
var isFoo:(_ a:T)->(Bool) 
...
}

所有的处理都将直接在我的Processing 类中实现,这看起来像是正确的解决方案,但现在又出现了另一个问题,这导致我采用了关联类型方法:事实证明,在许多情况下,我的Process 类的用户希望获得一些由我的框架实现的默认行为,例如,我可以为他们自动实现协议AB,只要他们的类符合协议X,它是这样做的:

protocol X:A,B {
...
}

extension protocol X {
   // Here was my default implementation of A and B, which enabled my user to directly get A and B implemented as long as their type conformed to X
}

通过使用这种方法,我可以让我的用户直接选择他们想要自己实现的东西,通过遵守协议X 他们只需要编写一点代码,让我的框架通过本身,如果他们想实现自己的 A 或 B,他们仍然可以。

所以如果我是对的,我的闭包实现就没有办法做这样的事情。

因此,出于这个原因,我认为关联类型协议将是一个很好的解决方案,因为在这里我可以让我的用户轻松获得一些默认行为或编写自己的行为,所以现在我们回到我最初的问题:

protocol AProcessing {
    associatedtype U
    func mix(_ lhs:U, _ rhs:U) -> U 
}

protocol BProcessing {
   associatedtype U
   func isFoo(_ a:U) -> Bool
}

然后做类似的事情:

class Process<T> {
var aProcessor:AProcessing<T>
var bProcessor:BProcessing<T>
}

与闭包相比,这里的优点是我可以编写一个符合 AProcessing 的特殊类,它可以提供默认实现,这样:

class AutomaticAProcessing<T:X>:AProcessing { ... }  

那会让我的用户做这样的事情:

var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing<SomeType>()
processData.bProcessor = TheirOwnImplemtation  

这不仅在 Swift 中是不可能的,而且感觉就像我使用了太多的“hacks”来完成事情,应该有一个更简单的语言功能来做到这一点,不幸的是我不知道我应该做什么使用。

【问题讨论】:

  • 认为你应该把它写成class B&lt;U&gt; { var doSomething&lt;U:A&gt; },你会没事的
  • @Honey 它也不编译!看起来&lt;U:A&gt; 无效。
  • 这个问题太长了,@TrevörAnneDenise。我鼓励你删除它,并提出一个新问题,限制在 15 个字以内。如果你不能用 15 个字问出来,那就不是问题。
  • 这看起来真的像你沿着错误的轴切分了问题。声明“但我希望 fooProcess 和 barProcess 执行不同的操作,这在许多情况下需要更改 MyType 的 A 和 B 协议的实现”尖叫“将协议视为语法包”。 (oleb.net/blog/2016/12/protocols-have-semantics) 我会完全重新考虑Process,并可能以不同的方向组合,可能使用高阶函数(即创建和返回其他函数的函数)。这通常是一种更强大的组合流程的方式。
  • @Napier 实际上我的 Process 类与实际进程无关,它只是这样调用,因为它处理数据

标签: swift


【解决方案1】:

我认为不可能,因为协议的泛型类型是在实现它的类中指定的。

你可以这样写:

class B<U, P: A> where P.T == U {
    var someVar: P?
}

但是您需要为特定类指定第二个参数。例如:

class C: A {
    typealias T = String

    func computeSomething(with: String) -> Double {
        return 0.0
    }
}

let b = B<String, C>()
let c = b.someVar

但它无法在其associatedtype 中返回具有特定类型的协议

【讨论】:

  • 实际上不需要第二个通用占位符。只要有&lt;P : A&gt;,并在typealias U = P.T 的类中创建一个typealias
【解决方案2】:

一种方法是从一个空的泛型结构开始,然后在有意义的类型上扩展它:

struct A<T> {}

extension A where T: Base {
  func computeSomething(with: T) -> Double {
    return 1
  }
}

用法:

protocol Base {}

class B<U: Base> {
  let doSomething = A<U>()

  func foo(x: U) -> Double {
    return doSomething.computeSomething(with: x)
  }
}

class Z : Base {}

let x = B<Z>()
let y = x.foo(x: Z())
print(y)

【讨论】:

    【解决方案3】:

    编辑#2:

    删除关联类型,应该是可行的:

    protocol A {}
    protocol B {}
    
    protocol AProcessing {
      func mix(_ lhs: A, _ rhs: A) -> A
    }
    
    protocol BProcessing {
      func isFoo(_ a: B) -> Bool
    }
    

    那么,你的处理器:

    class Process<T: A & B> {
      var aProcessor: AProcessing!
      var bProcessor: BProcessing!
    
      func foo(_ a: T) -> Bool {
        let x = aProcessor.mix(a, a)
        guard let b = x as? B else { return false }
        return bProcessor.isFoo(b)
      }
    }
    

    及用法:

    struct AutomaticAProcessing : AProcessing {
      func mix(_ lhs: A, _ rhs: A) -> A { return lhs }
    }
    
    
    struct TheirOwnImplemtation : BProcessing {
      func isFoo(_ a: B) -> Bool { return false }
    }
    
    
    struct SomeType : A, B {}
    
    var processData = Process<SomeType>()
    processData.aProcessor = AutomaticAProcessing()
    processData.bProcessor = TheirOwnImplemtation()
    
    let x = SomeType()
    let y = processData.foo(x)
    print(y)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多