25

i have some code in an object constructor similar to

delegate DataSet MyInvoker;

public MyObject(Param1 p1)
{
    // property sets here
    // ...

    BeginMyAsyncMethod();
}

public void BeginMyAsyncMethod() 
{
    // set some properties
    // ...

    MyInvoker inv = new MyInvoker(SomeBeginMethod);
    inv.BeginInvoke(new AsyncCallback(SomeEndMethod), null);
}

My questions are:

  1. Is this generally considered bad practice?
  2. Would it be better (or good) practice to define a start method in my class which the user would call to carry out the async action?

This answer gives me the impression that leaving it to the user is bad practice although I am talking specifically about starting async methods in the constructor, not about the correct construction of an object.

Community
  • 1
  • 1
ghostJago
  • 3,381
  • 5
  • 36
  • 51
  • 4
    Yes because you're potentially introducing unexpected behaviors. Objects should be constructed and free from exceptions and unexpected behavior. If the async actions are still happening after the object is constructed and the user is trying to use it... – Dustin Davis Aug 31 '11 at 17:50
  • This is not thread safe, so it would not be best practice. The inv object can be read by another thread while the BeginMyAsyncMethod is running. Thus getting in a conflict state where one thread is reading and one thread is writing, or any of the other hazards: http://en.wikipedia.org/wiki/Hazard_(computer_architecture) – Automatico Jan 04 '13 at 12:13
  • 9
    The responses here are awful (JNixon not withstanding). Everyone basically says, "Why would you want to do this?! It's bad practice!". Here's why you would want to do this: you can't do ANYTHING in .NET 4.5 without an await (IO, internet, a lot of dialog stuff, etc) and usually these things get loaded in the constructor. This is a good question and the answers here are bad. – micahhoover Mar 18 '13 at 14:35
  • 2
    @micahhoover I think that's the point. Your constructor shouldn't be *doing* anything other than initializing the object. I would think this would be the difference between instantiating an HttpClient versus making a request. – Ryan Gates Oct 07 '15 at 15:14

6 Answers6

23

This is easily accomplished with a slightly different approach. In all reality, this happens all the time, doesn't it? Here's a simple solution to give you an option without doing something dumb:

public class MyResource
{
    // do this instead of a constructor
    public async Task<MyResource> StartAsync()
    {
        await Task.Delay(1);
        return this;
    }
}

public class MyControl
{
    public MyResource Resource { get; set; }
    async void Button_Click(object s, EventArgs e)
    {
        // call start as if it is a constructor
        this.Resource = await new MyResource().StartAsync();
    }
}
Blaise
  • 21,314
  • 28
  • 108
  • 169
Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
14

It's typically never a good idea to do something in your constructor that is not directly related to creating the object instance. For example, if I didn't know the purpose of your MyObject class, I might not know that it spawns an asynchronous process when I create a new instance. That's generally a bad practice.

It almost sounds like you're saying; hey, create this object, and it's only going to be usable when this async process is finished; that's counter-intuitive.

If you want to do something like this, I would think you would be definitely served better by going to a factory pattern; you call a factory like so and it will create the object and call the method for you:

 var instance = MyObjectBuilder.CreateInstance();

 // Internally, this does
 //    var x = new MyObject();
 //    x.Initilizatize();
 //    return x;

If your object wont be usable until it's been finished initializing, then you should probably expose a property to check if it is ready, like so:

 instance.WaitForReady(); // blocking version
 if(instance.IsReady) // non blocking check
Tejs
  • 40,736
  • 10
  • 68
  • 86
8

Yes because you're potentially introducing unexpected behaviors. Objects should be constructed and free from exceptions and unexpected behavior. If the async actions are still happening after the object is constructed and the user is trying to use it...

Also, what if the async method is still running and the user decides to destroy the object?

A good read: http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613/ref=sr_1_1?s=books&ie=UTF8&qid=1314813265&sr=1-1

If your async method has to be async because it's a long running process then it needs to be moved to it's own method that gets called by the user, not when the object is instatiated.

You also have the potential issue of keeping a reference to the object if the user decides to destroy it before async method is done which means you could have a memory leak.

If this HAS to be done to initialize the object should be created using a factory or a builder.

Dustin Davis
  • 14,482
  • 13
  • 63
  • 119
  • 1
    The user can't decide to destroy the object -- only forget of it's existence ;-) If they have the object anyway then consider it implements IDisposable so it would be no different than any other object that needs to be "cleaned up". Also, why would BeginAsyncX throw an exception? (I don't disagree with the conclusion, but...) –  Aug 31 '11 at 17:57
  • @pst you don't know if it will or not. Thats the point. Depends on what the method does. But, it shouldnt be doing anything that requires resources, can potentially error out and should not modify any state of the object. – Dustin Davis Aug 31 '11 at 17:58
  • How is it any different then starting say, a Timer? –  Aug 31 '11 at 18:00
  • I disagree. If coded correctly you can drastically improve the performance of your UI to the user while still fetching data on the back side. You do have to dispose of your threads correctly though when the user destroys the objects prematurely. See my post here. http://tsells.wordpress.com/2011/03/31/cancel-backgroundworker-threads-when-closing-windows-applications/ – tsells Aug 31 '11 at 18:18
  • That above being said - this should not not be attempted in production code by inexperienced programmers. – tsells Aug 31 '11 at 18:19
  • 2
    @tsells it isn't a question of if it can be done or done correctly, but if it's bad practice in context of best practices and the answer is yes, this is a bad practice. Not a question of what benefits async methods provide to the UI. If this HAS to be done this way then a factory should be used, not doing this in a constructor. – Dustin Davis Aug 31 '11 at 18:33
5

It's definitely a bad idea to initiate anything in a constructor that might call back into the instance on another thread before the instance is fully constructed. Even if it's the last line of your constructor, the callback might be invoked before a subclass constructor finishes running since subclass constructor code runs after the base class constructor code. Even if your class is sealed today, it might be unsealed in the future, and tracking down this sort of problem if a subclass is added in the future would most likely be very expensive.

Basically, if you don't want to end up hunting heisenbugs, don't ever, ever do this sort of thing from a constructor. Instead, add a "start" method like you mention in your question #2.

Nicole Calinoiu
  • 20,843
  • 2
  • 44
  • 49
5

I agree, bad idea. Add an Initialize method. You can also add a static method that returns the new object and executes the Initialize method. In this way you can also keep the constructor as private.

public class XXX
{
    private XXX()
    {
    }

    private void DoMyWeirdThingsAsynchronously()
    {
        ....
    }

    public static XXX PerformSomethingStrange()
    {
        XXX result = new XXX();
        result.DoMyWeirdThingsAsynchronously();
        return result;
    }
}
Salvatore Previti
  • 8,956
  • 31
  • 37
  • 3
    I really like this suggestion. It is basically the same as Tejs, but a bit clearer by including the private constructor(s). Also, even though it does not use the async/await keywords which it easily could (answer was given in 2011), I think it is a better design option than JNixons because it encapsulates the responsibility of the behind-the-scenes async initialization rather than requiring the caller to 'know' about it (i.e. construct an instance and then call an async initialization method). – mdisibio Mar 27 '14 at 02:43
0

Personally I would avoid putting such code in a constructor. It would be better to provide a specific method for starting the asyncronous operation. Throwing exceptions from a constructor (other than validation exceptions) is a bad idea.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Not that I am arguing against the conclusion, but why would BeginAsyncX throw an exception? –  Aug 31 '11 at 17:55