swift 尝试自动化类型转换

im9ewurl  于 2023-09-30  发布在  Swift
关注(0)|答案(2)|浏览(74)

问题是,func类型(of:)导致了一个错误,尽管它返回了Int,并且应该将Any转换为Int

func cast<T>(_ value: Any, to type: T.Type) -> T { value as! T }

var item: Any = 5
cast(item, to: type(of: item)) // causes an error instead of returning Int

有没有办法做到这一点?使用ObjC可以帮助解决这个问题吗?

dphi5xsq

dphi5xsq1#

我怀疑这里有一个关于类型的基本误解。我把这个叫做“下一句是什么?“问题。考虑:

func f<T>(value: Any, type: T) {
    let casted = value as! type
    // ... ok, now what?
}

这是无效的,但让我们假设它是允许的。在这一点上,你会对casted做什么?下一行代码会是什么样子?什么方法可以在不知道类型的情况下调用?你能把它传递给哪个函数,它接受的限制比Any更严格?它的类型是type,当然,但这并不能为您带来任何东西。这和Any没什么区别因为你对它的形状一无所知。这就是类型告诉你的:可以对该值做些什么。
大部分问题可能来自你的前提:“如果我们需要转换Any类型...”你应该问为什么你一开始就有Any。如果你认为你可以安全地使用as!,你必须知道值的类型。为什么Any参与其中?回到那一步,重新设计以摆脱Any,许多类型问题就会消失。
对于你问题的后半部分,只考虑签名:

func function<T>() -> T

这意味着调用者可以请求任何存在的类型,并且此函数将返回一个。什么都行。它可以是内部Apple类型,不公开公共初始化。它可能是你从未听说过的自定义模块中的随机类型。它可能是一个无人居住的类型(如Never),字面上不能构造,因为故意没有该类型的值。这个函数保证它将返回一个。即使在理论上,也没有办法实现这个函数,除了调用fatalError()并崩溃。
几乎可以肯定的是,您希望返回某个固定类型列表中的一个。你的意思不是Any(例如,你不会返回CBPeripheral)。大多数人的意思是“类似JSON的类型,Stings,Numbers,Arrays,诸如此类的东西。”如果是这样,那么这就是一个协议。
这种模式的一个很好的例子是RangeReplaceableCollection。它需要一个返回空集合的init()方法。语义在这里很重要。Protocols are not just bags of syntax.使用RRC,您可以按照您所描述的方式编写有意义的算法:

func function<T: RangeReplaceableCollection>() -> T {
    var item: T = T()
    return item
}

您还没有给出一个实际的例子来说明您试图用casted做什么,所以我将猜测您的用例,如果这是错误的道路,您可以告诉我,您可以解释您所指的实际问题。也许你现在有这样的东西:

func addOne(value: Any) -> Int {
    let intValue = value as! Int
    return intValue + 1
}

addOne(value: 4)

你不想写as! Int,而只想让它成为“无论运行时类型是什么”。所以你写:

func addOne(value: Any) -> Int {
    let intValue = value as! type(of: value)
    return intValue + 1
}

但这行不通。如果我调用addOne(value: "x"),你希望发生什么?在这种情况下,as!将成功(它不会失败,因为它是自己的类型)。但是+会做什么呢?如果我传递CBPeripheral或UIViewController会怎么样?这就是Swift类型可以帮助我们避免的全部问题。如果这可以在Swift中工作,那么甚至不需要as!。你可以只使用Any,它会在运行时被发现,并像JavaScript一样做一些“事情”(可能会崩溃)。
或者你可能想写一些东西来处理“任何数字类型”,并试图这样写:

func addOne(value: Any) -> type(of: value) {
    let intValue = value as type(of: value)
    return intValue + 1
}

但没有用。那是因为Any是错误的类型。Any几乎总是错误的类型。相反,您需要泛型和协议,它们可以直接安全地处理此问题:

func addOne<Value>(value: Value) -> Value
where Value: AdditiveArithmetic & ExpressibleByIntegerLiteral {
    return value + 1
}

那就不需要选角了如果您发现自己经常调用as!,那么您可能使用了错误的类型。

rryofs0p

rryofs0p2#

考虑你的第一个例子:

var item: Any = 5
var casted = item as! type(of: item)

为了避免混淆,让我们假设你有一个otherItem作为铸造蓝图,它提供了一个具体的类型,那么是的,你可以这样做,但你必须使用Swift提供的静态类型工具,在这种情况下,泛型和.self来获得示例的类型。
下面是一个示例(灵感来自here):

func cast<T>(value: Any, to type: T) -> T {
    return value as! T
}

let item: Any = "this is a test"
let otherItem: String = "Hello World"
let otherType = otherItem.self // or explicit: String.self()
let casted = cast(value: item, to: otherType)

关于你的第二个问题:为了在函数中定义泛型类型的示例,编译器必须知道如何初始化它。因此,它不适用于不提供任何信息的无约束泛型类型T。但是,有一种方法可以做到这一点:

  • 创建一个需要初始化器init(a:, b:, ...)的协议Initializable(无论您需要什么参数)
  • 将协议作为约束添加到函数中,即func function<T: Initializable>() -> ...
  • 现在,可以在函数中调用let x = T.init(a:, b:, ...),因为可以确保类型T提供了这样的初始化器。

相关问题