Sign in to follow this  
luke2

C# Static Members Don't 'Register' At Program Startup (unlike C++)

Recommended Posts

Alrighty, so I've been trying to make an factory for resource loading similar to the one presented in 'Why Pluggable Factories Rock My MultiPlayer World' in the featured section, in C#. So I have it all fine, with a Loader class that is abstract. So I made my first concrete loader, SurfaceLoader (images), defined as:
using System;
using System.Collections.Generic;
using System.Text;
using SdlDotNet.Graphics; 

namespace Iodine
{
    class SurfaceLoader : Loader
    {
        private static SurfaceLoader surfaceLoader;

        public SurfaceLoader( )
            : base( "bmp" )
        {

        }

        protected override Object createResource( String fileName )
        {
            Surface surface = new Surface( fileName );
            return surface; 
            
        }
    }
}

Now, the constructor for the static member surfaceLoader isn't called at program startup, if I delete it, and call the constructor in the client code somewhere, then it works just fine... but the whole point of the static member was to make it unnecessary for that. So, is there anyway to force the building, and subsequent instantiation of the static member, or is there some other workaround? This is in Visual Studio 2005 Team Edition

Share this post


Link to post
Share on other sites
Classes are only created lazily... when they are used for the first time. So the first time you use a static method in your class, or create a new instance of it, it will use the static constructor once.

If you want to have stuff load up at startup and register, your best bet is to reflect through your loaded assembly(ies) and do your registry like that.

Share this post


Link to post
Share on other sites
I've done 'scripting' w/CodeDom, so what class should I use to reflect through my own code? (also, I would expect that I could distinguish between classes that have, and have not been derived from Loader?)

Share this post


Link to post
Share on other sites
The most common way to support a "reflection-based plugin system" is to write a custom attribute and adorn the relevant classes or methods with it. If you are doing classes they will probably also inheitit / implement a certain interface too, but thats a seperate part of the decision.

So like:

EngineAddInAttribute could take a LoadBehavior enum telling it to behavior telling it to LoadAtStartup or LoadOnDemand ... if such a system is needed, or just a parameterless StartupMethodAttribute could be used to adorn any parameterless static methods you want to execute ...

and you'd use reflection (start at the Assembly class usually) to find types or methods which have the corresponding attributes.

So many choices :)

I have to say when I converted my C++ framework to C# that static meaning shift caused me major headaches and brain dumps for months ... then somewhere along the way my brain started designing my frameworks to be a little less hardcoded and more flexible, and I find that I use a system now more like the ASP.NET web app system (where each app puts a little bit of code in AppStartup and AppShutown if they want to - like Framework.LoadPlugins() ...)

Share this post


Link to post
Share on other sites
Thanks guys, I've fixed it with your help!
So, I did the reflection thing, and although I didn't really understand what Xai meant by a custom attribute- All the loaders were subclasses of Iodine.Loader, and it just so happened that there was a isSubClassOf( Type ) method :)))

So here is the code that I placed into the Loader class:

public static void initializeChildren( )
{
Assembly assembly = Assembly.GetExecutingAssembly( );
Type[] types = assembly.GetTypes( );

for ( int i = 0; i < types.Length; i++ )
{
if ( types[ i ].IsSubclassOf( Type.GetType( "Iodine.Loader" ) ) )
types[ i ].GetConstructor( Type.EmptyTypes ).Invoke( null );
}
}

Share this post


Link to post
Share on other sites
Surely this would be an ideal place to use generics? (This doesn't help your problem, but your class appears to return Object). As in;

using System;
using System.Collections.Generic;
using System.Text;
using SdlDotNet.Graphics;

namespace Iodine
{
class SurfaceLoader : Loader<Surface>
{
private static SurfaceLoader surfaceLoader;

public SurfaceLoader( )
: base( "bmp" )
{

}

protected override Surface CreateResource( String fileName )
{
Surface surface = new Surface( fileName );
return surface;

}
}


class Loader<T> {
public virtual T CreateResource(string filename) {
return default(T);
}
}

}

Share this post


Link to post
Share on other sites
I'm sorry, but I don't really understand-- If I made my loader class generic, than I would end up with client code that would have several different versions of Loader- since Loader<x> != Loader<y>. And I would merely replace a typecast( object = (GameObj)Loader.getResource(..) -> gameObj = Loader<GameObj>.getResource(..) )

Also, why are you redefining a method of Loader, and indeed the class itself I think?
I've probably overlooked some significant but small fact of C# generics..

Share this post


Link to post
Share on other sites
just to show you a quick sample of the attribute stuff I was talking about. here's some code:


[AttributeUsage(AttributeTargets.Class)]
public class StartupPluginAttribute : Attribute
{
}

public interface ISelfRegisteringPlugin
{
void RegisterPlugin();
void ReleasePlugin();
}

public static class PluginLoader
{
public static void RegisterPluginType(Type pluginType)
{
// whatever you need to do ...
}

public static void RegisterPlugins_UsingAttribute()
{
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach(Type type in assembly.GetTypes())
{
object[] attributes = type.GetCustomAttributes(typeof(StartupPluginAttribute), false);
StartupPluginAttribute spa = (attributes.Length == 0) ? null : attributes[0] as StartupPluginAttribute;
if(spa != null)
RegisterPluginType(type);
}
}
}

public static void RegisterPlugins_UsingInterface()
{
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach(Type type in assembly.GetTypes())
{
if(type.IsSubclassOf(typeof(ISelfRegisteringPlugin)))
RegisterPluginType(type);
}
}
}

[StartupPlugin]
public class SomePlugin : ISelfRegisteringPlugin
{
#region ISelfRegisteringPlugin Members

void ISelfRegisteringPlugin.RegisterPlugin()
{
// Do Stuff
}

void ISelfRegisteringPlugin.ReleasePlugin()
{
// Do Stuff
}

#endregion
}


Share this post


Link to post
Share on other sites
Ah, I see what you meant by attributes- I'll modify my loader/relfection code to use that as opposed to isSubClassOf(..).

Although, what are those things that are outside of any class inside of [..]?
I've seen, what I guess are class modifiers or somesuch, things like:
[sealed] //Looked it up, makes class uninheritable etc

Share this post


Link to post
Share on other sites
This things in square brackets ARE attributes ...

if you see [ Searializable ] on top of a class, it means that class has an instance of the SerailizableAttribute attached.

All Attribute classes should end there name with the "Attribute" suffix and inherit from System.Attribute ... and then when using them, you leave off the Attribute suffix (C# knows to do the lookup as if you typed the word attribute on the end - syntactic sugar).

P.S. There is nothing wrong with the interface / subclass version - I just wanted you to understand the attribute system, because a lot of things in .NET use it, and you should know how too. For making this decision I ask myself which of 3 cases I'm in. Case 1, it would NEVER make sense to implement the interface in a class and yet not have that class register itself at startup, Case 2, most are all classes that implement the interface will want to register themselves automatically, Case 3, The application itself will need to decide which plugins etc are registerd. In case 1 inheiritance or attributes work - inheiritance is slightly easier, in case 2 inheiritance or attributes work - attributes are more flexible, in case 3 inheiritance or attribute can be used to "find" plugings, but some other code must explicitly list or choose those to load.

Also, attributes do 1 thing that inheirtance is less good at ... lets you set additional values on the attribute ... for instance - testing a class to see if it implements the ICoolStuff interface is boolean, it either does or it doesn't. Getting a CoolStuffAttribute off the class lets you look at values in the attribute ... such as:

[ CoolStuff("Name", 3, false) ]
public class LaserGuidedBomb
{
}

but really its easy to overdo attributes - anything that can be done with attributes can be done the conventional way ... attributes are ONLY preferred for metadata ... those things which ancillary systems might want to know about a class that are NOT core to the classes operation. Otherwise you end up with stupid systems where you are sorting instances in a list by their type's Category attribute instead of by their Category member ... and this way leads to madness.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this