在 Swift 中将抽象类型转换为具体类型

Convert Abstract Type to Concrete Type in Swift

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

我的应用有包含客户信息的客户模型,还包含 his/her 付款来源。 API 给了我两种支付方式:cardbank account 它们有完全不同的字段。

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

我需要将我的抽象类型放在一个框中并将其用作具体类型 (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 给我错误的描述如下:

Using 'PaymentSource' as a concrete type conforming to protocol 'PaymentSource' is not supported

我哪里做错了?

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

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

Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code.

意味着已经能够将协议视为类型

可能是:

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 在你的情况下)。