The sample code shows one of the gazillion ways it's possible to cause an object to be 
in an "illegal" (a.k.a. undefined or unexpected) state -- a state that violates one or 
more "class invariants" (even if not formally defined).  Being exception-ignorant 
(whether inside the constructor or elsewhere) is one way to do that.  Software that 
allows objects to get into undefined states (causing class invariants to be violated) 
-- in this case, the "if all steps of object initialization are not complete, the 
object should not be referenced by other objects" class invariant -- has bugs.  IMHO, 
there's nothing particularly special about this particular way of causing a bug.

The fact that an object exists during execution of its constructor is pretty much a 
given -- otherwise you couldn't set properties of the object, or call its methods, 
during the constructor.

The fact that an object exists until it's garbage collected (whether or not an 
exception is raised in the constructor, or in any other method) is the way things work 
in .NET.

The fact that it's possible to store a reference to an object that is in an illegal 
state, and thus prolong the lifetime of that "bad" object by preventing an object from 
being GCd, doesn't surprise anyone -- does it?

If you don't work carefully to avoid problems with an object's state being internally 
inconsistent, you can cause objects with "bad state" to exist.  That's why it 
sometimes matters what order you write the lines of code in your methods.  What is new 
or different or surprising here?

At 07:53 AM 4/4/2004, John Elliot wrote (in part)
>[snip]
>Someone commented that 'the object is not constructed' if an exception
>is thrown from the constructor, but that's not true. It's true that a
>reference to the new instance is not returned, but the instance is
>always 'created' (I'm not sure what the definition is 'construction' is
>in this case, but certainly an object id is allocated, etc.). This is
>obviously the case, since you have access to the 'this' pointer inside
>the constructor.
>
>I hacked something together to check this out. As a result I've come up
>with a new coding rule for myself: 'if the constructor is going to
>throw, then the constructor has to guarantee that it hasn't configured
>another instance with a reference to itself'. Of course, this is
>immediately impractical given the way the WinForms designer and
>InitializeComponent() works. Hmm.. not sure what to do.. In my view, at
>least to some extent, construction should be 'atomic'. Consider the
>example below, which leaks references to 'partially constructed'
>instances to the invocation list of an event. This could cause all sorts
>of crazy things to happen that would be difficult to diagnose.
>
>If you run this code, you'll see three partially constructed classes
>handle the publisher's event. This is definitely something to watch out
>for.
>
>At the end of the day, I tend to think of exceptions as being too
>difficult to handle. I design my code such that exceptions don't happen.
>If they do, then I have a bug and I need to fix it, I don't pretend that
>I can really 'handle' an exception (obviously there are cases where you
>have to, but I try to avoid these), despite its politically correct name
>I still consider it an 'error' and I generally terminate my process. To
>try and handle all the possible states that you could be in when you
>receive an exception seems almost impossible to me.
>
>John.
>
>  public class EntryPoint {
>
>    [STAThread]
>    public static void Main(string[] args) {
>
>      EventPublisher publisher = new EventPublisher();
>      EventSubscriber subscriber = null;
>
>      // construct and detach three subscribers
>      for (Int32 i = 0; i < 3; i++) {
>
>        try {
>          // try to construct a subscriber
>          subscriber = new EventSubscriber(publisher);
>        }
>        catch {
>          // ignore construction exception
>        }
>
>        if (subscriber != null) {
>          // construction was successfull, now detatch
>          // NOTE: this won't execute in this example as construction
>          // is never successful, meaning we can't detach the
>          // subscriber
>          subscriber.Detach();
>        }
>      }
>
>      // notify all subscribers of publishers event
>      // NOTE: three 'partially constructed' instances will be notifed
>      // although the code on this method tends to indicate that there
>      // should be no subscribers (assuming that construction is
>'atomic'
>      // to some extent).
>      publisher.Notify();
>
>      Console.ReadLine();
>    }
>  }
>
>  public class EventPublisher {
>
>    public event EventHandler SomeEvent;
>
>    public void Notify() { this.OnSomeEvent(EventArgs.Empty); }
>
>    private void OnSomeEvent(EventArgs e) { if (this.SomeEvent != null)
>this.SomeEvent(this, e); }
>
>  }
>
>  public class EventSubscriber {
>
>    private EventPublisher _publisher;
>    private Boolean _isConstructed;
>
>    public EventSubscriber(EventPublisher publisher) {
>
>      // reference publisher
>      this._publisher = publisher;
>
>      // subscribe to publishers event (has the effect of passing
>      // a reference to to this presently partially constructed
>      // instance to the publisher)
>      this._publisher.SomeEvent += new
>EventHandler(this.SomeEventHandler);
>
>      if (this._publisher != null) {
>        // throw an exception during construction
>        // NOTE: this is just for the sake of example
>        throw new ApplicationException();
>      }
>
>      // flag construction as successfully completed.
>      // NOTE: this will not be set and the value will remain
>      // defaulted to 'false'.
>      this._isConstructed = true;
>
>    }
>
>    public void Detach() {
>
>      // if still attached, detach from the publisher
>
>      if (this._publisher != null) {
>
>        this._publisher.SomeEvent -= new
>EventHandler(this.SomeEventHandler);
>        this._publisher = null;
>
>      }
>    }
>
>    private void SomeEventHandler(Object sender, EventArgs e) {
>
>      // NOTE: for testing output the hashcode (inherited from Object
>      // as the internal object id) and an indication of successful
>      // instance creation.
>      Console.WriteLine("Instance " + this.GetHashCode() + " handled
>event. Constructed:" + this._isConstructed);
>
>    }
>  }


J. Merrill / Analytical Software Corp

===================================
This list is hosted by DevelopMentorŪ  http://www.develop.com
Some .NET courses you may be interested in:

NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles
http://www.develop.com/courses/gaspdotnetls

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to