C#, is there 'Defer Call' like golang?
Asked Answered
A

8

13

golang support 'Defer Call' and when c++, I use this trick(?).

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

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

how can I do this in c# ?

I need like this golang defer tutorial

Amity answered 19/11, 2016 at 7:49 Comment(8)
What is the aim of defer call, here, it will continue later post some other action?Scaliger
Although the trick can be possibly used for multiple purposes, the cleanup it is often used for in C# is implemented with explicit IDisposable interface.Maze
Please note @WiktorZychla point is very important, in C# cleanup may need a finalizer which automatically cleans, if user fails to calls Dispose method explicitly, so you may not do anything in this regard in C#, it will clean when no more referencedScaliger
@MrinalKamboj: what you just said is that there are no defers in C#. What I say is what is the idiomatic way of cleanups defers are often used for.Maze
@MrinalKamboj check this link please.. golang defer tutorialAmity
@WiktorZychla you may say by virtue of C# implementation it has implicit Defer, but by no means I am saying there's no Defer. Most of the Dispose driven clean up action has to be explicitly called by the used failing which it will lead to a leakScaliger
@MrinalKamboj: true, devs fail to call explicit dispose methods. By no means Tasks and async/await doesn't help here. In your answer you focus on language feature, somehow ignoring what defers are used for.Maze
@WiktorZychla if Defer does only clean up work, then yes I am wrong, Task, has nothing to do with it, but if its about saving a work to be done later, as understood from the definition, then Task have a role to play out hereScaliger
S
-5

I picked up the following definition of the Defer Call in GoLang from here

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

C# version:

Option 1:

  • Use a Task API
  • Wrap your DeferCall method in the Task
  • Execute at the right time either Manually using Start() method or for the function post which you want to continute, wrap that in another Task object and execute using ContinueWithAPI, which will chain tasks to execute one after another

Option 2:

  • Can make whole call chain Async-Await, where everything post await keyword is executed the main call finish, this is standard Asynchronous implementation
Scaliger answered 19/11, 2016 at 7:59 Comment(7)
Tasks are not an option here. "The list of saved calls is executed after the surrounding function returns". Tasks don't behave like this.Petepetechia
Why would Task not work like this, you can certainly execute them once a call return, just that it has to be explicitly executed, using Start or` ContinueWith, not an implicit continuation like Async-AwaitScaliger
I'm a down-voter too. It's clear, that defer-panic-recover are finally-throw-catch in C#. How this is related to tasks? Do you use tasks in your C# code to perform cleanup? Why did you read "defer" word, but omitted the whole sentence from the link you referred?Petepetechia
@Petepetechia That would be the exact replacement if we are only talking about clean up, but as I can understand from the definition pasted above, it is more about keeping a call aside and use it later, which is, mostly used for clean up, but not the only thingScaliger
You're stubborn man, do you know it? :) Again, defers are used to execute, when surrounding method returns. They are mostly used for cleanup. Do you use tasks for cleanup?Petepetechia
Alright, I have no experience of Go-Lang, you might have used these APIs. I have certainly misunderstood the deferred execution in this case. Thanks for all the clarification.Scaliger
Can't be accepted answer without understanding what Go's defer does.Nickelodeon
P
17

The nearest language equivalent will be try-finally:

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

The nearest framework equivalent will be IDisposable + using:

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

Personally, defer term confuses me, since this is not "deferred execution" (schedule now - execute later, which could be implemented via tasks), but some sort of RAII implementation.

P.S. Assuming, that you will continue to learn golang:

  • panic equivalent is throw
  • recover equivalent is catch
Petepetechia answered 19/11, 2016 at 8:1 Comment(2)
using was the first thing that came to my mind. It was made for pretty much the same scenarios that C++ destructors and (I'm assuming) Go defer calls were created for.Bodkin
@Amity this question should be approved. The current approved answer doesn't actually answer your questionDairy
N
15

You can implement this using System.Reactive.Disposables.Disposable

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

    WriteFile(fileName);
    ConvertFile(fileName, "OutputFile.txt");
}
Noontide answered 25/11, 2021 at 8:32 Comment(3)
I don't see "defer" being called anywhere.Karakorum
@tuket: defer is disposed/called when going out of scope, which is the closing curly brace.Noontide
Ah, I understand now. Thanks!Karakorum
G
1

No, but not impossible defer is a keyword designed to execute functions when its parent function returns and starts executing when return statement is reached.

Since defer in go works like a stack, so calling deferred functions in-order results in a reverse order.

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

It's best usecases are closing file readers, database transactions, and stacking logs where closing before returning is necessary.

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

Workarounds would be like Dennis' answer: https://mcmap.net/q/855585/-c-is-there-39-defer-call-39-like-golang

Gallman answered 5/8, 2019 at 8:24 Comment(0)
G
0

Not a built-in functionality or keyword in C# but you can create a defer engine which executes the deferred actions after method call is returned,
Jess's golang defer usage example you can write in C# equivalent like:

using System.Reflection;

DeferTest test = new DeferTest();
Defer.Execute(()=> { test.Test(); });

public class DeferTest
{
    [Deferrable]
    public void Test()
    {
        int i = 0;

        Defer.Me(() => { Console.WriteLine("1"); });
        Defer.Me(() => { Console.WriteLine("2"); });
        i++;
        Defer.Me(() => { Console.WriteLine("3"); });
        Defer.Me(() => { Console.WriteLine("4"); });

        if (i > 0)
            return;

        Defer.Me(() => { Console.WriteLine("5"); });
    }
}

public class DeferrableAttribute : Attribute
{
    public DeferrableAttribute()
    {
        DeferList = new Defer();
    }

    public DeferrableAttribute(Defer d)
    {
        DeferList = d;
    }
    public Defer DeferList;
}

public class Defer
{
    private static Stack<Action> Actions { get; set; }
    static Defer() { Actions = new Stack<Action>(); }

    public static void Me(Action a)
    {
        Actions.Push(a);
    }

    public static void Execute(Action action)
    {
        action();
        while (Actions.Count > 0)
        {
            var a = Actions.Pop();
            a();
        }
    }
}

enter image description here

advanced version: https://gist.github.com/nitinjs/10ce0ee2e377c88c8a9bde1ab9440336

Gromme answered 31/3, 2023 at 17:12 Comment(0)
J
0

First: you need to follow the language patterns, in that case, using an IDisposable object.

Second: I have been written about defer in C#, follow this link https://medium.com/@schivei/implementing-go-style-defer-in-c-32ac86c4ac57

In this article, I’m using the (using) keyword, without brackets, and it’s acts the same way in golang (disposing/executing) a code lambda on the scope ends.

Jockey answered 23/4, 2023 at 16:51 Comment(0)
I
0

Ok, several good answers, but I think I can help a little bit.

The short answer is: No, you don't have anything like golang defer in C# nor C++ (even with tricks - unkes you hack the call stack in C++ than you can have).

defer in go put's the execution of the defered function in the return of a function (just before returning). It's not scope aware (if you call a defer inside a if in go, it will be executed when the whole function return and not when the if ends for example).

But, you can use the try-finally or the dispose pattern to get a similar behaviour as explaned by other users in previous answers.

Isfahan answered 11/8, 2023 at 19:34 Comment(0)
C
-2

You can use Goto, which isn't a sin. Here's a function with multiple points of exit, and some cleanup that must be run on every exit.

public void DoWork()
{
    bool result = false;

    // Some work that wants to return false
    goto wrap;

    // Some other work that wants to return true
    result = true;
    goto wrap;

    // Some other work that wants to return false
    goto wrap;
    
wrap:
    DoCleanup();
    return result;
}
Chandelier answered 31/3, 2023 at 15:15 Comment(0)
S
-5

I picked up the following definition of the Defer Call in GoLang from here

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

C# version:

Option 1:

  • Use a Task API
  • Wrap your DeferCall method in the Task
  • Execute at the right time either Manually using Start() method or for the function post which you want to continute, wrap that in another Task object and execute using ContinueWithAPI, which will chain tasks to execute one after another

Option 2:

  • Can make whole call chain Async-Await, where everything post await keyword is executed the main call finish, this is standard Asynchronous implementation
Scaliger answered 19/11, 2016 at 7:59 Comment(7)
Tasks are not an option here. "The list of saved calls is executed after the surrounding function returns". Tasks don't behave like this.Petepetechia
Why would Task not work like this, you can certainly execute them once a call return, just that it has to be explicitly executed, using Start or` ContinueWith, not an implicit continuation like Async-AwaitScaliger
I'm a down-voter too. It's clear, that defer-panic-recover are finally-throw-catch in C#. How this is related to tasks? Do you use tasks in your C# code to perform cleanup? Why did you read "defer" word, but omitted the whole sentence from the link you referred?Petepetechia
@Petepetechia That would be the exact replacement if we are only talking about clean up, but as I can understand from the definition pasted above, it is more about keeping a call aside and use it later, which is, mostly used for clean up, but not the only thingScaliger
You're stubborn man, do you know it? :) Again, defers are used to execute, when surrounding method returns. They are mostly used for cleanup. Do you use tasks for cleanup?Petepetechia
Alright, I have no experience of Go-Lang, you might have used these APIs. I have certainly misunderstood the deferred execution in this case. Thanks for all the clarification.Scaliger
Can't be accepted answer without understanding what Go's defer does.Nickelodeon

© 2022 - 2025 — McMap. All rights reserved.