Making a constructor sometimes illegal to call

Suppose you write a control subclass that you want people to be able to add to a window layout OR be able to create on the fly. But you want to make sure that when an instance is constructed in code it MUST have a parameterized constructor. However, in order to put an instance on a layout by drag and drop it has to have a no parameter constructor. These two design goals seem very much at odds.

So how do you make this happen ?

Such a control might be like :

Class myControl
  Inherits Canvas

  Sub Constructor()
  End Sub

  Sub Constructor(name as string)
     self.mName = name
  End Sub

  Computed Property MyName as string
    Function Getter() as string
       return mName
    End Function
    Sub Getter(value as string)
       mName = value
    End Sub
  protected mName as string
end class

We’ll assume that MyName has been exposed as part of the Inspector Behaviour.

But the class definition above would definitely allow you to write code that called either constructor. How to make it so the no parameter constructor can only be used by instances on a layout that, as part of initializing the layout, basically does the following

dim c as new myControl
c.MyName = "whatever value the user set in the inspector"

But there is a trick you can use to know how you have been called. An exception exposes the call stack to you and you can examine that to know if there is a window being initialized that has resulted in your no parameter constructor being called. And so you can make it so a window being initialized can use the no parameter version. In fact to make it so your control can be placed on a layout in the IDE designer it MUST have a no parameter constructor if it has any constructors.

If we make that no parameter constructor read as follows we can make this raise an exception when used improperly outside of a window being constructed. Sorry can’t make the compiler reject it (THAT would be awesome)

  Raise New NilObjectException
Catch noe As NilObjectException
  Dim stack() As String = noe.Stack
  // IF this is an instance created on a layout we should find a 
  // Window.__Init%%o<Window> in the stack above the call to this Constructor
  For i As Integer = 0 To stack.Ubound
    If stack(i) = "Window.__Init%%o<Window>" Then
    End If
  // and if we do not then raise an UnsupportedOperation error
  Raise New UnsupportedOperationException
End Try

A window, when being created, calls __Init and so we can tell if we were called by a window being created or not by walking up the call stack. If there is no Window.__Init in the stack then we definitely were not and so can raise an exception to let the coder know that this usage, without a parameter, is only for Layouts and nothing else.