c# - 在保证无损的情况下,Swift 是否有任何隐式类型转换机制?
问题描述
我试图找出 Swift 是否可以像 C# 和其他语言一样支持类型之间的隐式转换,而这些类型已知是完全兼容和无损的。ExpressibleByXxxLiteral
协议似乎很接近,但我不认为它们实际上是我所追求的。更多内容在最后。
但是,现在,请考虑以下DistanceUnit
枚举。这是我创建的,目的是让我的 API 的用户ARKit
——它基于米——以他们选择的任何单位指定值。如果他们想要在他们的增强场景中将某些东西移动两英寸,他们不必手动进行数学计算。他们只是使用moveBy(x: .inches(2))
并且枚举通过其公开的meters
属性自动转换它,并将其传递给期望它们的Apple API。
简而言之,将其视为 SceneKit 中仪表的用户友好、美化类型别名。
enum DistanceUnit {
case meters(SCNFloat)
case centimeters(SCNFloat)
case millimeters(SCNFloat)
case feet(SCNFloat)
case inches(SCNFloat)
case points(SCNFloat)
var meters:SCNFloat {
switch self {
case let .meters(meters) : return meters
case let .centimeters(cm) : return cm * 0.01
case let .millimeters(mm) : return mm * 0.001
case let .feet(feet) : return feet * 0.3048
case let .inches(inches) : return inches * 0.0254
case let .points(points) : return (points / 72) * Self.inch.meters
}
}
// Presets
static let meter : DistanceUnit = .meters(1)
static let centimeter : DistanceUnit = .centimeters(1)
static let millimeter : DistanceUnit = .millimeters(1)
static let foot : DistanceUnit = .feet(1)
static let inch : DistanceUnit = .inches(1)
static let point : DistanceUnit = .points(1)
}
注意:由于 SceneKit 的类型
SCNVector3
基于平台的不同类型,因此我创建了SCNFloat
我的 API 标准化的类型别名,因此无论平台如何它都可以正常工作,而无需在我的代码中进行这些编译器检查,因为它在类型别名处发生一次定义。
#if os(iOS)
typealias SCNFloat = Float
#else
typealias SCNFloat = CGFloat
#endif
此外,由于这种类型最终代表仪表,为了让用户更简单,特别是在将默认值等内容添加到自己的函数时,我还实现了ExpressibleByIntegerLiteral
and ExpressibleByFloatLiteral
,就像这样......
extension DistanceUnit : ExpressibleByIntegerLiteral {
init(integerLiteral value: Int) {
self = .meters(SCNFloat(value))
}
}
extension DistanceUnit : ExpressibleByFloatLiteral {
init(floatLiteral value: Float) {
self = .meters(SCNFloat(value))
}
}
回到问题...
我讨厌在我的 API 内部,value.meters
在传递时我总是必须这样做DistanceUnit
。我已经知道这个枚举是通过 a 表达的米的美化类型别名SCNFloat
,那么为什么我总是必须明确地输入它呢?
我试图找出的是是否有可能以某种方式装饰这种类型,以便它可以直接传递给任何期望SCNFloat
像 C# 这样的语言一样的 API。
C#方式
现在在 C# 中,这很容易使用隐式转换运算符。SCNFloat
这是和之间隐式转换的示例DistanceUnit
(从技术上讲,我在这里混合了一些 Swift 伪代码来说明这一点,因为 C# 没有具有关联值的枚举,但你明白了)......
public static implicit operator DistanceUnit(SCNFloat value) => .meters(value);
public static implicit operator SCNFloat(DistanceUnit units) => units.meters;
上面的第一行说“任何接受 a 的 APIDistanceUnit
现在都可以直接接受 a SCNFloat
。传递 aSCNFloat
时,编译器会自动将该值插入到.meters(x)
枚举案例中,然后该枚举值最终传递给期望DistanceUnit
.'的接收者。
相反,第二行说'任何接受 a 的 APISCNFloat
现在都可以DistanceUnit
直接接受 a。传递 aDistanceUnit
时,编译器将自动提取.meters
属性,该属性已经是一种类型SCNFloat
,并将简单地将该值传递给期望 a 的接收者SCNFloat
。
简单、可预测且完全无损。
注意:上面我已经定义了两种方式的转换——从
SCNFloat
到DistanceUnit
和反之亦然。但是,如果没有意义,您不必同时指定两者。例如,期望 a 的东西
Float
可以很容易地进行转换Int
,因为这样的转换是无损的,但是期望 a 的东西Int
不一定会在Float
不丢失一些数据的情况下进行。在这些情况下,您可以将其定义为单向,或者您仍然可以将它们定义为双向,但您将使用
explicit
而不是标记有损方向implicit
。这使得用户必须显式地将一种类型转换为另一种类型,警告他们这可能是一个有损操作,但由他们来进行调用。如果数据不能保证 100% 无损,则永远不要使用。implicit
回到斯威夫特维尔...
这些ExpressibleByXxxLiteral
协议起初看起来像是成功了一半,但实际上,我认为这是一条红鲱鱼,因为它实际上并没有在运行时进行转换。提示在名称中...'literal',意思是在代码中按字面意思输入的东西,而不是Xxx
. 游乐场似乎也支持这一理论。这让我相信,虽然对于一些简单的类型化文字很方便,但这实际上并不是我在这里所追求的解决方案。
那么,当您知道一种类型与另一种类型完全兼容时,Swift 是否具有任何此类特性/功能,无论是单向还是双向的?
解决方案
推荐阅读
- c# - 调用 Vendor API 5000 次的最佳方法
- r - 在 ShinyWidgets 中使用 selectizeGroup 时可以添加选项组吗?
- android - 无法从 androidx 导入 androidx.annotation.NonNull 和所有
- google-data-studio - Google Data Studio - 混合数据添加非连接字段
- ibm-cloud - 如何将外部卷连接到 IBM Cloud Code Engine
- domain-driven-design - 在用例中使用基础设施
- visual-studio - MFC:Visual Studio 2019 提示无法实例化 ActiveX 控件?
- java - 类中的Java @Autowire 依赖项并在类成员初始化中使用它们
- ansible - Ansible-playbook 搜索错误目录中的角色!如何纠正它?
- json - 将配置绑定到复杂对象