Swift中等同于C# using语句的语法

7

我对Swift语言非常陌生,但有C#的编程背景。

我想知道在Swift语言中是否有类似于C# using语句的等价代码。

using( var a = new MyClass()){
//Code Here
}

你应该解释 using 的作用。不要指望 Swift 程序员知道 using 在退出时清理了 a - Panagiotis Kanavos
实际上,你必须解释一下使用的原因,这不仅可以确保清理代码被调用。 - Panagiotis Kanavos
@PanagiotisKanavos 这正是使用的作用。它只调用 Dispose(),没有其他操作。 - Alexander
@Alexander,你一直试图用defer的术语来解释using。但它们是不同的语言,不同的设计,不同的语义。nothing else保证了确定性调用并创建了一个额外的作用域。这也意味着你不需要遵循使用defer时需要遵循的约定。这意味着编译器本身确保有一个Dispose方法,并且它总是被调用。 - Panagiotis Kanavos
@PanagiotisKanavos using 不会创建新的作用域。你需要自己使用 { } 显式地创建,惊喜的是,在 Swift 中你也可以这样做。这也意味着你不需要遵循使用 defer 时需要的约定 嗯?这意味着编译器本身确保有一个 Dispose 方法,并且它总是被调用 编译器对于在 defer 块中调用的任何方法都会这样做,完全一样。 - Alexander
2个回答

5

你可以将清理代码放在类的deinit方法中。

在许多GC系统(如CLR上的C#)中,这是不被鼓励的,因为不能保证GC会很快(或根本不会)运行。虽然ARC有其他缺点,但这正是它的亮点:Swift的自动引用计数保证了去初始化的确定性。这与C++中的RAII完全相同。

即使抛出异常,此技术也能正常工作。

class MyClass() {
    var db = openDBConnection() //example resource

    deinit() {
        db.close()
    }
}

func foo() {
    var a = MyClass()
    print(a) // do stuff with a

    // the (only) reference, a, will go out of scope,
    // thus the instance will be deinitialized.
}

你也可以使用defer语句:

var a = MyClass()
defer { a.cleanUp() /* cleanup a however you wish */ }

你失去了像 IDisposable 这样使用接口的标准化,但你获得了能够执行任何你想要的代码的一般性。


1
这并不等同。using 会在退出块时调用类的 Dispose 方法,即使有异常也是如此。使用 defer 则需要手动编写清理代码。 - Panagiotis Kanavos
1
Panagiotis,你必须在Dispose重载中手动编写它。我不明白你的意思。 - Bauss
@Bauss,应该很清楚。Dispose是一个类方法,因为它可以访问内部字段,所以可以清理它们。使用defer时,您必须重复清除代码。此外,作用域不同。using确保在退出其块时调用Disposedefer不能提供这样的保证。您可以使用using在不再需要连接时立即关闭连接。您无法使用defer执行相同的操作。 - Panagiotis Kanavos
使用using语句块将在退出块时调用类的Dispose方法,即使有异常也是如此。延迟语句块将在出现异常时运行。这就是整个重点所在。使用延迟语句块需要手动编写清理代码。而Dispose方法会自己编写吗?当然你必须编写自己的清理代码。使用延迟语句块不需要重复清理代码。你可以创建一个方法来进行清理,并且只需调用该方法即可。 - Alexander
实际上,using只是一个语法糖,编译器会将其转换为try/finally块。这强制执行对象的处理,释放内存。如果不处理对象,则不会释放任何内存。在Swift中,我似乎理解到处理内存的方法有所不同(使用RC而不是GC),因此实际上不需要Swift版本的“using”语句,因为不存在这种语句。应该学习如何按照Alexander解释的方式处理内存。 - Phate01
显示剩余2条评论

2

我正在学习Swift,遇到了同样的问题。

我找到的最接近的答案是:

   if let UOW = (try UnitOfWork() as UnitOfWork?)
   {


   }

这是一种可选绑定的hack方法,但似乎可以工作。您需要确保您的类定义了一个与Alexander上面所述相同的deinit方法。我发现即使我的init抛出异常,只要您离开IF语句的范围,deinit方法仍然会被调用。

注意: 如果适用,确保使用弱引用以确保实际调用deinit!

对于您的作用域,使用DO块可能更符合Swift风格:

do
{
    let UOW = try UnitOfWork()
    // your code here
}

这种方式的好处是避免了使用块时出现“金字塔般的噩梦”(如在C#中可能会出现的情况)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接