当前位置: 首页 > 面试题库 >

如何通过通用的初始化程序在相关类型之间进行转换?

庾奇思
2023-03-14
问题内容

我正在尝试建立一个可以相互转换的类型家族。例如,Float和Double可以通过其初始值设定项相互转换。我不想创建一个详尽的初始化列表,以显示每种类型都可以转换为每种其他类型。

我试图在Playground中做类似的事情,但是崩溃了:

protocol FloatConvertible {
    init(_ x:FloatConvertible)
}

extension FloatConvertible {
    init(_ x:FloatConvertible){self.init(Self(x))}
}

extension Float:FloatConvertible {}
extension Double:FloatConvertible {}

func transmute<T:FloatConvertible, U:FloatConvertible>
    (a:T, b:U) -> T {
    return T(b)
}

transmute(Float(3.1), b: Double(2.6))

我的最终目标不仅仅是做转换,但乘以a通过b像这样:

func *<T:FloatConvertible, U:FloatConvertible> (a:T, b:U) -> T{
    return a * T(b)
}

这样我就可以表示乘法。

有没有办法做到这一点?我认为问题的一部分是由看起来像这样的结构来解决的Double(Double(Double(Double(...))),但是我认为我不能施加确保的约束T != U


问题答案:

问题是,在您的中init(_ x:FloatConvertible),Swift无法推断具体的类型x。它只是知道这是一个FloatConvertible。因此,当您尝试做时Self(x),虽然它
可以 推断的具体类型Self,但它不知道您要调用哪个初始化程序,这意味着它将默认为您的init(_ x:FloatConvertible)初始化程序,从而造成无限循环。

如果为自定义初始化程序指定一个参数名称,则会看到Swift抱怨找不到正确的初始化程序:

protocol FloatConvertible {
    init(c x:FloatConvertible)
}

extension FloatConvertible {
    init(c x:FloatConvertible) {
        // error: missing argument name 'c:' in call
        // (i.e it can't find the concrete type's initialiser)
        self.init(Self(x)) 
    }
}

一个 潜在的
,因此解决方案是通过在运行时来解决这个switch荷兰国际集团在具体类型x可以。但是,这不像静态解决方案那样好,因为您可以从提高的安全性以及某些情况下提高的性能中受益。

为了静态地执行此操作,您可以_asOther在协议中添加一个通用的“影子”函数,该函数可以将给定的浮点类型转换为另一种浮点类型,也可以将具体类型的初始化程序添加到协议要求中。

这将使您不必列出所有可能的转换组合-您现在可以_asOther从初始化程序中调用。

protocol FloatConvertible {
    init(_ other:Float)
    init(_ other:Double)
    init(_ other:CGFloat)
    init(fromOther x:FloatConvertible)

    func _asOther<T:FloatConvertible>() -> T
}

extension FloatConvertible {
    init(fromOther x:FloatConvertible) {self = x._asOther()}
}

// note that we have to implement these for each extension,
// so that Swift uses the concrete types of self, preventing an infinite loop
extension Float : FloatConvertible {
    func _asOther<T:FloatConvertible>() -> T {return T(self)}
}

extension Double : FloatConvertible {
    func _asOther<T:FloatConvertible>() -> T {return T(self)}
}

extension CGFloat : FloatConvertible {
    func _asOther<T:FloatConvertible>() -> T {return T(self)}

    // note that CGFloat doesn't implement its own initialiser for this,
    // so we have to implement it ourselves
    init(_ other:CGFloat) {self = other}
}

func transmute<T:FloatConvertible, U:FloatConvertible>(value: T, to: U.Type) -> U {
    return U(fromOther: value)
}

let f = transmute(value: CGFloat(2.6), to: Float.self)
print(type(of: f), f) // prints: Double 2.59999990463257

在初始化程序中,_asOther将在输入值上调用,并self针对通用参数推断类型T(在这种情况下self,保证为具体类型)。_asOther然后,该函数将在on上调用x,它将返回该值作为给定的目标类型。

请注意,您 不必fromOther:在自定义初始化程序中使用参数标签-
在没有任何标签的情况下仍然可以使用。尽管我强烈建议在编译时使用它来捕获代码中的任何问题(否则Swift会接受在运行时会导致无限循环的代码)。

另外请注意,您可能应该重新设计,以了解*过载的工作方式。返回您输入的更精确的类型(即Float * Double = Double)会更有意义-
否则,您不必要地失去了精度。



 类似资料:
  • 我刚开始使用ReactJS,遇到了一个小问题。 我的应用程序本质上是一个带有过滤器的列表和一个更改布局的按钮。目前我使用三个组件:

  • 问题内容: 我得到了错误: ImageLoader必须使用配置进行初始化,然后才能使用“非法状态异常”中的错误。 我正在努力将gridview放在片段中以显示图像。根据我的理解,应该首先通过扩展将要扩展的类AbsListViewBaseActivity来初始化。 这两个类用于初始化。我很弄乱流程,并且在运行时遇到错误,并在两天内进行了处理。我将包括LogCat错误。 我自己的版本 - 主班 程序的

  • 我尝试将Jenkins作业设置为使用BrowserStack运行测试。在本地,测试运行良好,驱动程序初始化,会话创建,测试开始运行,使用“。在Jenkins安装了一个插件。但下面的错误存在。 如果我在本地设置则存在以下错误 在本地设置后,错误将指向以下行 我希望通过Jenkins与BrowserStack的连接应该在本地完成。 提前道谢。

  • 问题内容: 我是程序世界中的每个新手,我遇到一个问题,我的要求与使用JAVA Code在Android平板电脑与台式机之间的通信有关。 上面的代码是我的servlet代码,它在我的本地系统服务器(Tomcat 6.0)中运行,在这里,我正在通过println发送消息,并且我想在另一个系统中运行的Android应用中显示相同的消息。现在,我要发布在另一个系统上运行的android代码。 这里的192

  • 我正在尝试使用数据绑定在Kotlin中实现MVVM arcitecture。代码只是将空白屏幕显示为输出。有人能帮我弄清楚为什么在执行这个程序时没有对服务器的调用吗。 我尝试了这个和类似的链接来解决这个问题。 我的代码如下: 主要活动 } CvRepository类 } }

  • 问题内容: 在 pipe 上使用2个 队列 在进程之间进行通信有什么优势(如果有)? 我正在计划使用python模块。 问题答案: 最大的好处是队列是进程和线程安全的。管道不是:如果两个不同的进程试图读取或写入pipe的同一端,则会发生不良情况。队列的抽象级别也比管道更高,这在您的特定情况下可能有优势,也可能没有优势。