【问题标题】:Convert Abstract Type to Concrete Type in Swift在 Swift 中将抽象类型转换为具体类型
【发布时间】:2017-04-12 08:12:04
【问题描述】:

我正在尝试为我的应用制作数据模型。这是场景:

我的应用具有包含客户信息的客户模型,还包含他/她的付款来源。 API 为我提供了两种支付来源:cardbank account,它们具有完全不同的字段。

所以,这是我的问题,我想要一个抽象类型,即 PaymentSource,然后在每个 PaymentSource 中都有一个函数来返回转换为它的类型的对象。一些我是如何键入擦除的。

我需要将我的抽象类型放在一个盒子中,并将其用作具体类型 (AnyPaymentSource)。

所以,我做了以下工作:

protocol PaymentSource {
    associatedtype Kind
    func cast() -> Kind
}

struct AnyPaymentSource<PS: PaymentSource> {
    private var paymentSource: PS
    init(paymentSource: PS) {
        self.paymentSource = paymentSource
    }
    func cast() -> PS.Kind {
        return paymentSource.cast()
    }
}

struct Card: PaymentSource {
    func cast() -> Card {
        return self
    }
}

struct BankAccount: PaymentSource {
    func cast() -> BankAccount {
        return self
    }
}

struct Customer { 
    var firstName: String
    var lastName: String
    var email: String
    var paymentSource : AnyPaymentSource<PaymentSource> 
}

但是Customer 给了我以下描述的错误:

不支持将“PaymentSource”用作符合协议“PaymentSource”的具体类型

我哪里做错了?

【问题讨论】:

  • 泛型类型PS 必须是具体类型,例如BankAccountCard,而不是它所遵循的协议。
  • @vadian 那么为什么要为客户的 PaymentSource 创建抽象类型呢?我需要在不知道它是什么类型的情况下存储付款来源。然后稍后在代码中获取类型
  • 这些PaymentSource 结构的目的是什么?事实上,对于这两种付款方式,您最好使用enum
  • @XmasRights true 我可以将枚举与泛型一起使用,但后来我必须使用大量的 switch case。我正在寻找更优雅的解决方案,例如关联类型

标签: swift generics type-erasure swift-protocols associated-types


【解决方案1】:

斯威夫特是statically typed language。这意味着在编译时必须知道变量的类型。

当我遇到这个问题时,我解决了这个问题

protocol PaymentSource {
    associatedtype Kind
    func cast() -> Kind
}

struct AnyPaymentSource<PS: PaymentSource> {
    private var paymentSource: PS
    init(paymentSource: PS) {
        self.paymentSource = paymentSource
    }
    func cast() -> PS.Kind {
        return paymentSource.cast()
    }
}

struct Card: PaymentSource {
    func cast() -> Card {
        return self
    }
}

struct BankAccount: PaymentSource {
    func cast() -> BankAccount {
        return self
    }
}

struct Customer<T:PaymentSource> {
    var firstName: String
    var lastName: String
    var email: String
    var paymentSource : AnyPaymentSource<T>
}
func test(){
    let customerWithCard = Customer<Card>(
        firstName: "",
        lastName: "",
        email: "",
        paymentSource: AnyPaymentSource(paymentSource: Card())
    )
    let customerWithBankAccount = Customer<BankAccount>(
        firstName: "",
        lastName: "",
        email: "",
        paymentSource: AnyPaymentSource(paymentSource: BankAccount())
    )
    print(customerWithCard.paymentSource.cast())
    print(customerWithBankAccount.paymentSource.cast())
    return
}

【讨论】:

  • aaw 谢谢你,我会把你的答案和这个blog上的东西混在一起@
  • 对我来说,我认为这里有一种“代码味道”。提到cardCustomer = Customer&lt;Card&gt;(...AnyPaymentSource(paymentSource: Card()) 似乎不好,你在这里提到同样的事情
  • @AhmadF 这被称为类型擦除,甚至在 swift 标准库中使用(参见 here)还有更多示例,您可以查看 herehere
【解决方案2】:

如果您要实现的目标是@Andrew Ashurov 在他的回答中提到的,则无需实施AnyPaymentSource。正如Swift Protocols Documentation中提到的:

协议本身实际上并不实现任何功能。 尽管如此,您创建的任何协议都将成为成熟的类型 用于您的代码

已经能够将协议视为类型的意思。

可能是:

protocol PaymentSource {
    func cast() -> Self
}

struct Card: PaymentSource {
    func cast() -> Card {
        return self
    }
}

struct BankAccount: PaymentSource {
    func cast() -> BankAccount {
        return self
    }
}

struct Customer {
    var firstName: String
    var lastName: String
    var email: String
    var paymentSource : PaymentSource?
}

创造客户:

let cardCustomer = Customer(firstName: "Card Fname", lastName: "Card Lname", email: "cardemail@example.com", paymentSource: Card())

let bankAccountCustomer = Customer(firstName: "Bank Account Fname", lastName: "Bank Account Lname", email: "bankaccountemail@example.com", paymentSource: BankAccount())

请注意,在Customer 结构中,paymentSource 类型为PaymentSource 的属性,这意味着它可以分配为符合PaymentSource 协议的任何类型(在您的情况下为CardBankAccount)。

【讨论】:

  • 当您在客户的 paymentSource 上调用 cast 时,它会返回一个 PaymentSource。使用关联类型,我可以指定它应该转换为哪个类型。阅读此blog 了解详细问题和解决方案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多