【问题标题】:C#, is there 'Defer Call' like golang?C#,是否有像 golang 这样的“延迟调用”?
【发布时间】:2017-04-03 02:18:42
【问题描述】:

golang 支持“延迟调用”,当使用 c++ 时,我使用这个技巧(?)。

struct DeferCall
{
   DeferCall() {}
   ~DeferCall() { doSomeThing(); }
}

void SomeMethod()
{
    DeferCall deferCall;
    ...
}

如何在 c# 中做到这一点?

我需要这样的golang defer tutorial

【问题讨论】:

  • defer call的目的是什么,在这里,稍后会继续发布其他操作?
  • 虽然这个技巧可能有多种用途,但它在 C# 中经常使用的清理是通过显式的IDisposable 接口实现的。
  • 请注意@WiktorZychla 点非常重要,在C# 中清理可能需要一个自动清理的终结器,如果用户未能显式调用Dispose 方法,因此在C# 中你可能不会在这方面做任何事情,它不再引用时会清理
  • @MrinalKamboj:你刚才说的是 C# 中没有延迟。我要说的是通常用于清理延迟的惯用方式是什么。
  • @MrinalKamboj:是的,开发人员未能调用显式的处置方法。绝不是Tasks 和async/await 在这里没有帮助。在您的答案中,您专注于语言功能,以某种方式忽略了延迟的用途。

标签: c# destructor


【解决方案1】:

我从here 中获得了GoLang 中Defer Call 的以下定义

defer 语句将函数调用推送到列表中。保存的调用列表在周围函数返回后执行。 Defer 通常用于简化执行各种清理操作的函数。

C# 版本:

选项 1:

  • 使用Task API
  • 将DeferCall 方法包装在Task 中
  • 在正确的时间执行手动使用Start()方法或您想要继续的函数帖子,将其包装在另一个Task对象中并使用ContinueWithAPI执行,这将链接任务以一个接一个地执行

选项 2:

  • 可以使整个调用链Async-Await,其中await 关键字后的所有内容都执行主调用完成,这是标准的异步实现

【讨论】:

  • 任务在这里不是一个选项。 “保存的调用列表在周围函数返回后执行”。任务的行为不是这样的。
  • 为什么Task不能像这样工作,你当然可以在调用返回后执行它们,只是它必须显式执行,使用Start或`ContinueWith,而不是像这样的隐式延续Async-Await
  • 谁投了反对票,请给出明确的理由,以便我理解并改正
  • 我也是一个反对者。很明显,defer-panic-recover 在 C# 中是 finally-throw-catch。这与任务有何关系?您是否在 C# 代码中使用任务来执行清理?为什么您阅读了“推迟”这个词,但从您引用的链接中省略了整个句子?
  • 你是个固执的人,你知道吗? :) 同样,defers 用于在周围方法返回时执行。它们主要用于清理。您是否使用任务进行清理?
【解决方案2】:

最接近的等效语言是try-finally:

try
{
    DoSomething();
}
finally
{
    DoCleanup();
}

最接近的等效框架是IDisposable + using:

using (var stream = File.OpenRead("foo.txt"))
{
    // do something
}

就个人而言,defer 术语让我感到困惑,因为 这不是“延迟执行”(现在安排 - 稍后执行,可以通过任务实现), 但某种RAII 实现。

附: 假设你会继续学习 golang:

【讨论】:

  • using 是我首先想到的。它是为与 C++ 析构函数和(我假设)Go defer 调用创建的几乎相同的场景而设计的。
  • @ekflame 这个问题应该被批准。当前批准的答案实际上并未回答您的问题
【解决方案3】:

不,但并非不可能 defer 是一个关键字,旨在在其父函数返回时执行函数,并在到达 return 语句时开始执行。

由于 go 中的 defer 像堆栈一样工作,因此按顺序调用延迟函数会导致顺序相反。

package main

import (
    "fmt"
)

func main() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")

    if num := 2; num == 2 {
        defer fmt.Println("4")
        return
    }
    defer fmt.Println("5")
}

// 4
// 3
// 2
// 1

https://play.golang.org/p/jzHG-paytBG

最好的用例是关闭文件读取器、数据库事务和堆栈日志,在这些情况下需要在返回之前关闭。

var count
defer println(count)
for { // forever
    count++
    if count == 100 {
        return
    }
}

defer pkg.DoCleanup()
something := pkg.DoSomething()
pkg.Add(something.ForCleanup)
pkg.Add(something.ForCleanup)

stream, _ := file.OpenRead("text.txt")
defer stream.Close()

// stream closed
// pkg cleaned
// 100

解决方法就像丹尼斯的回答:https://stackoverflow.com/a/40690576/9640539

【讨论】:

    【解决方案4】:

    您可以使用System.Reactive.Disposables.Disposable 实现此功能

    void Function()
    {
        string fileName = "InputFile.txt";
        using var defer = Disposable.Create(() => { File.Delete(fileName); });
    
        WriteFile(fileName);
        ConvertFile(fileName, "OutputFile.txt");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2015-01-03
      • 2016-08-25
      相关资源
      最近更新 更多