Making sure static constructors have been called

  • Thread starter James Crosswell
  • Start date
J

James Crosswell

I want to create a class with a static property as follows:

class MyClass
{
private static List<MyHandler> RegisteredHandlers =
new List<MyHandler>;
}

I then want to be able to create descendants of the MyHandler class
which will register themselves using a RegisterHandler method, which
basically adds one and only one instance of such classes to the static
list of RegisteredHandlers.

Each MyHandler will be able to deal a particular kind of object and will
have a bool HandlesObject(object aObject) method that returns true/false
to say wether it's capable of dealing with that object or not.

Finally, I want to be able to loop through an array of objects, check to
see if there is a MyHandler registered for each and, if so, call
MyHandler.DoSomething(theObject);

Initially I figured I could have each of the MyHandler descendant
classes implement a static constructor as follows:

class SomeObjectHandler : MyHandler
{
static SomeObjectHandler()
{
MyClass.RegisterHandler(new SomeObjectHandler());
}
}

However, it seems static constructors are only guaranteed to be called
sometime after the program starts and before an instance of the object
that they apply to is instantiated. In my case, the only instance of
SomeObjectHandler that will be instantiated is the one that gets
instantiated in it's own static constructor... which means that the
approach above isn't going to work.

In Delphi/ObjectPascal (which I used to use) each code unit can have
Initialization/Finalization sections that contain code which will be run
when that executable or dll is run/terminated. This is basically what I
need here. Is there any equivallent to this in .NET/c#??? Basically I
need something that allows me to write some code which is guaranteed to
be run at program startup (and which isn't in static void Main() obviously).

Thanks in advance.

Best Regards,

James Crosswell
Microforge.net LLC
http://www.microforge.net
 
J

Joanna Carter [TeamB]

"James Crosswell" <[email protected]> a écrit dans le message de %23v%[email protected]...

|I want to create a class with a static property as follows:
|
| class MyClass
| {
| private static List<MyHandler> RegisteredHandlers =
| new List<MyHandler>;
| }

| In Delphi/ObjectPascal (which I used to use) each code unit can have
| Initialization/Finalization sections that contain code which will be run
| when that executable or dll is run/terminated. This is basically what I
| need here. Is there any equivallent to this in .NET/c#??? Basically I
| need something that allows me to write some code which is guaranteed to
| be run at program startup (and which isn't in static void Main()
obviously).

Heheh ! welcome to the Delphi to C# conversion experience :)

No, as far as I can discover, there is no implicit initialisation available
in C#; you really are going to have to, at least, call a static method or
property on each class that need registering; and that call would have to be
at the start of the static void Main() method..

May I suggest that, assuming that all the derived classes are in the same
assembly, that you use reflection to discover those classes that contain a
static "Register" method and call that method.

public static class HandlerRegister
{
...
public static void Initialise()
{
// get the classes from the assembly
// and if they contain a method "Register"
// call it
}
}

static void Main()
{
HandlerRegister.Initialise();

...
}

Joanna
 
B

Bruce Wood

James said:
I want to create a class with a static property as follows:

class MyClass
{
private static List<MyHandler> RegisteredHandlers =
new List<MyHandler>;
}

I then want to be able to create descendants of the MyHandler class
which will register themselves using a RegisterHandler method, which
basically adds one and only one instance of such classes to the static
list of RegisteredHandlers.

Each MyHandler will be able to deal a particular kind of object and will
have a bool HandlesObject(object aObject) method that returns true/false
to say wether it's capable of dealing with that object or not.

Finally, I want to be able to loop through an array of objects, check to
see if there is a MyHandler registered for each and, if so, call
MyHandler.DoSomething(theObject);

Initially I figured I could have each of the MyHandler descendant
classes implement a static constructor as follows:

class SomeObjectHandler : MyHandler
{
static SomeObjectHandler()
{
MyClass.RegisterHandler(new SomeObjectHandler());
}
}

However, it seems static constructors are only guaranteed to be called
sometime after the program starts and before an instance of the object
that they apply to is instantiated. In my case, the only instance of
SomeObjectHandler that will be instantiated is the one that gets
instantiated in it's own static constructor... which means that the
approach above isn't going to work.

In Delphi/ObjectPascal (which I used to use) each code unit can have
Initialization/Finalization sections that contain code which will be run
when that executable or dll is run/terminated. This is basically what I
need here. Is there any equivallent to this in .NET/c#??? Basically I
need something that allows me to write some code which is guaranteed to
be run at program startup (and which isn't in static void Main() obviously).

Funny, someone else posted a couple of days ago with a similar problem:

http://groups.google.com/group/micr...ges.csharp/browse_frm/thread/8133faa227fcb77a

Joanna's suggestion is a good one. Here's another, lower-tech solution:
in each of your assemblies, write a sealed (or static in .NET 2.0)
class with a known name that has just one static method: Register().
Write explicit code into the Register class to register each applicable
class from the assembly.

When you load an assembly, look for that particular class by name, and
find its Register static method. If the class isn't there, or it has no
Register static method, then the assembly is malformed.

Joanna's solution is more automatic and adaptive to change, while mine
is low-tech and easy to understand. I'm sure that there are other ways,
as well.
 
J

James Crosswell

Joanna said:
No, as far as I can discover, there is no implicit initialisation available
in C#; you really are going to have to, at least, call a static method or
property on each class that need registering; and that call would have to be
at the start of the static void Main() method..

May I suggest that, assuming that all the derived classes are in the same
assembly, that you use reflection to discover those classes that contain a
static "Register" method and call that method.

Thanks Joanna,

And Bugger. One of the reasons I'm doing it in this round-about fashion
is that the app I'm designing is modular in nature and each module is in
a separate assembly. I'm trying to design some code which could do
generic stuff for all of the modules that are loaded.

One thing I'm thinking here is I have a namespace:

namespace MyApp.Server

And the modules are all in sub-namespaces like:

namespace MyApp.Server.AccountingModule
namespace MyApp.Server.OrdersModule
namespace MyApp.Server.InventoryModule

Is there any way to use reflection to get a list of classes within those
sub-namespaces? If I could do that then I think I could achieve what I want.

Best Regards,

James Crosswell
Microforge.net LLC
http://www.microforge.net
 
J

James Crosswell

Bruce said:
Joanna's suggestion is a good one. Here's another, lower-tech solution:
in each of your assemblies, write a sealed (or static in .NET 2.0)
class with a known name that has just one static method: Register().
Write explicit code into the Register class to register each applicable
class from the assembly.

When you load an assembly, look for that particular class by name, and
find its Register static method. If the class isn't there, or it has no
Register static method, then the assembly is malformed.

That sounds like a perfect solution - if the assembly doesn't implement
a MyCustomRegistration method then it's not one that's meant to be
loaded by my host app. Thanks.
Joanna's solution is more automatic and adaptive to change, while mine
is low-tech and easy to understand. I'm sure that there are other ways,
as well.

Thanks - I'll see if I can do it using reflection as well but if not
then the solution above will probably work.

Best Regards,

James Crosswell
Microforge.net LLC
http://www.microforge.net
 
B

Bruce Wood

James said:
Thanks Joanna,

And Bugger. One of the reasons I'm doing it in this round-about fashion
is that the app I'm designing is modular in nature and each module is in
a separate assembly. I'm trying to design some code which could do
generic stuff for all of the modules that are loaded.

One thing I'm thinking here is I have a namespace:

namespace MyApp.Server

And the modules are all in sub-namespaces like:

namespace MyApp.Server.AccountingModule
namespace MyApp.Server.OrdersModule
namespace MyApp.Server.InventoryModule

Is there any way to use reflection to get a list of classes within those
sub-namespaces? If I could do that then I think I could achieve what I want.

I wouldn't worry about the namespaces. I haven't tried it, but the
thinking should go like this.

You just loaded the assembly, so you should be able to get your hands
on an Assembly object.
From the Assembly object you can call GetExportedTypes() to get all
types (classes and structs) that the assembly exports for public
consumption.

Now you can just iterate over the array of Types and use Reflection to
see which ones have static Register methods that take no arguments (or
has a signature that you recognize). Any type that has a static
Register() method, you call it via Reflection's Invoke().

The upside to this technique is that you can add a new class to an
assembly, give it a static Register method, and it will be registered.
You don't have to remember to add it to a central Register method for
the whole assembly, so nothing is ever missed => lower maintenance.

The downside is that if you forget about this mechanism and add a
static Register() method to some class for some other reason, it will
be called when the assembly is loaded. Another downside is that all of
these Register methods are (or should be) public, which means that
they're now individually callable by client code. If you use a central
Register() method for the whole assembly, the individual Register()
methods for the classes can be internal and so hidden from outside
callers.
 
J

Joanna Carter [TeamB]

"Bruce Wood" <[email protected]> a écrit dans le message de (e-mail address removed)...

Thanks Bruce for your additional info, yet another way of skinning the same
cat :)

Joanna
 
D

Dave Sexton

Hi James,

Another solution might be to create a custom attribute that takes a single
type. You can check if the attribute is defined on the assembly, and if so
load the type. The type could implement an interface such as IStartupType
and you could call the method like that. This has several benefits over
reflection, but it requires the startup assemblies to reference some common
assemby in order to reference the interface and attribute.

// Define the attribute and interface in a shared assembly:

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)]
public class StartupTypeAttribute : Attribute
{
private Type type;
public Type Type { get { return type; } }

public StartupTypeAttribute(Type type)
{
this.type = type;
}

public IStartupType GetStartupType()
{
return (IStartupType) Activator.CreateInstance(type);
}
}

public interface IStartupType
{
void RunAtStartup();
}

// implement the interface in another assembly:

class SomeType : IStartupType
{
public void RunAtStartup() { ... }
}

// register SomeType in the same assembly:

[assembly: StartupType(typeof(SomeType))]

// in the main assembly you can load all of the startup types:

// assuming "assembly" is an instance of System.Reflection.Assembly instance
foreach (StartupTypeAttribute attribute in
assembly.GetCustomAttributes(typeof(StartupTypeAttribute), false))
{
attribute.GetStartupType().RunAtStartup();
}

This way it's somewhat type-safe, so at runtime you'll get real exception
info instead of TargetInvocationException if an error occurs in one of the
startup modules as happens when using reflection.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top