Abstract
Many developers use third party application frameworks to help take the grunt
work out of creating the guts of software. One fine example is the Microsoft
Foundation Classes for C++. But what if you want to "roll your own" framework? A
nice feature of MFC is the ability to create arbitrary document types and
associated views on the fly. This article will discuss one method of dynamic
object creation for just such a purpose. I think it's a fitting topic for a
new Year()
:-)
This article assumes you are moderately accomplished in the .NET Framework.
Visual Studio.NET is not required, and in fact, I don't use it here. I suppose
it's my CLI bias (that's Command Line Interface for those
of you keeping score.) While this sample code is written in C#, it can be
applied to any .NET language.
There will be a lot of code, so bear with me, and please note that error
handling is omitted in the sample code. In real life I'd always
include error handling.
The Right Tool
Sir Winston Churchill once said, "Give us the tools and we shall finish the
job." Well, we certainly have the right tool in the
System.Reflection
assembly, and it's name is
ConstructorInfo
. A little digging and you find that
System.Type
can be used to find this tool on any .NET type. Once we
have the tool, we can call it's Invoke()
method and voilá! We get
back an object
.
A Base Document
Armed with the tool, we can forge ahead with some code. For this article,
we'll be creating a "document manager" capable of dynamically creating and
managing requested document objects. To do so, we need to define what a document
is. We'll define a base document class from which we can derive all our
documents. This base class will feature: an ID, a title, a "path", a modified
flag; definitions for basic persistence; and an event to notify interested
parties that it's been modified:
public delegate void DocumentModifiedHandler(Document theDoc);
public class Document
{
private Guid m_Id;
protected string m_strTitle;
protected string m_strPath;
protected bool m_bModified;
public Document()
{
m_Id = Guid.NewGuid();
m_strTitle = "";
m_strPath = "";
m_bModified = false;
}
public Guid Id { get { return m_Id; } }
public string Title
{
get { return m_strTitle; }
set
{
m_strTitle = value;
FireModified();
}
}
public string Path { get { return m_strPath; } }
public bool IsModified
{ get { return m_bModified; } }
virtual public void Save(string path)
{
m_strPath = path;
m_bModified = false;
}
virtual public void Load(string path)
{
m_strPath = path;
m_bModified = false;
FireModified();
}
protected void FireModified()
{
if (this.Modified != null) Modified(this);
}
public event DocumentModifiedHandler Modified;
}
Type Info
On our way to dynamic creation, we need some way to specify what type of
document is available for creation. We create a class to handle this need:
public class DocumentType
{
private Guid m_Id;
private string m_strName;
private string m_strExtension;
private Type m_DocType;
public DocumentType(string name, string extension, Type type)
{
m_Id = Guid.NewGuid();
m_strName = name;
m_strExtension = extension;
m_DocType = type;
}
public Guid Id { get { return m_Id; } }
public string Name { get { return m_strName; } }
public string Extension
{ get { return m_strExtension; } }
public Type DocType { get { return m_DocType; } }
}
Notice that we squirrel away a System.Type
. This is key for our
framework. We also store some information that could be used to assist in
displaying "new" document types for the UI portion of our framework.
The Glue
The last piece of the framework we need to implement is some way to manage
all the documents and their creation. For this we define a base
DocumentManager
class that has the basic functionality we need:
- Management of document types including registering new types, removing
types, and their enumeration.
- Management of the documents themselves, including creation, removal and
enumeration.
I won't list the entire class, just the most interesting part -- dynamic
document creation:
virtual public Document CreateDocument(Guid doctypeid)
{
DocumentType oDocType = (DocumentType)m_DocTypes[doctypeid];
if (null == oDocType) return null;
System.Reflection.ConstructorInfo oConstructor =
oDocType.DocType.GetConstructor(Type.EmptyTypes);
Document oDoc = (Document)oConstructor.Invoke(null);
m_Documents.Add(oDoc.Id, oDoc);
return oDoc;
}
We now have our framework bits!
The important thing here is the call to GetConstructor
. If we
wanted an alternate constructor, we'd create an array of Types containing the
constructor parameter types. I leave selecting from multiple constructors up to
the reader. For our framework, the default constructor is sufficient, and makes
things simple. For a dynamic "view" framework similar to this one, a ViewManager
could use a View constructor that takes one argument of type Document.
Wielding the Tool
The sample code creates some Document derivatives and uses them to illustrate
using our framework. It's important to register each Document we intend on
creating dynamically. The following code shows how a documents are registered:
DocumentManager oDM = new DocumentManager();
oDM.RegisterDocType("Text File", "txt", typeof(TextDocument));
oDM.RegisterDocType("Image File", "jpg", typeof(ImageDocument));
oDM.RegisterDocType("Remotely Stored File", "", typeof(RemoteDocument));
We should probably squirrel away the IDs returned by the registration process
so we can use them later. The sample code just enumerates them for creation. If
all compiled well, we should see the following output when we run the sample
code:
Registered Document Types 3:
Remotely Stored File, nspace.Samples.Framework.RemoteDocument,
1fad0238-cbe6-4428-999d-8652f6039807
Image File, nspace.Samples.Framework.ImageDocument,
8d5b4b55-4401-4b90-8a6f-2478e5cd8404
Text File, nspace.Samples.Framework.TextDocument,
b0f5f9fc-fbd0-4a62-bea3-d31153d9ec21
Creating instances of each doc type...
Created a remote document with Id fc223fa0-d312-4052-8119-c844d0c72950
Created an image document with Id 2f9f33d4-ec13-418d-a5b7-3330be660060
Created a text document with Id 8275ac8e-a1e4-4f3f-9691-e8ba4fa1d2b0
Of course, the GUIDs you will see will be different whenever you run the
sample.
Closing Remarks
Application frameworks can be daunting to design and implement. Hopefully
this article has taught you a bit about the .NET Framework and whetted your
appetite for creating useful and reusable code. I recommend reading the
.NET Framework Documentation for further information.
Stay tuned for future articles...
Building the Sample Code
Unzip the source files to the folder of your choice. Start a shell (cmd.exe)
and type nmake. You may have to alter the Makefile to
point to the correct folder where your .NET Framework libraries exist.
History
- 2003-01-13 Initial Revision / Doc Formatting
References
- Microsoft .NET Framework SDK Documentation
20+ years as a strategist at the intersection of business, design and technology.