C# - Forcing static symbols within a library to load

Started by
5 comments, last by Telastyn 12 years, 10 months ago
Hi All,

I have an c# implementation of a pluggable factory similar to the C++ implementation on here in that it uses static registration objects to register types with the factory, for example:


static PluggableFactory<string, ControlBase>.Maker<ButtonControl> Maker = new PluggableFactory<string, ControlBase>.Maker<ButtonControl>("Button Control");


Within the same assembly this pattern works fine, however as with the C++ implementation it falls over when you place the code within a library and attempt to reference it: the static objects don't get created!

Is there method I can use of forcing these objects to create when they are within an assembly I am referencing?

Thanks,
Scott
Game development blog and portfolio: http://gamexcore.co.uk
Advertisement

Hi All,

I have an c# implementation of a pluggable factory similar to the C++ implementation on here in that it uses static registration objects to register types with the factory, for example:


static PluggableFactory<string, ControlBase>.Maker<ButtonControl> Maker = new PluggableFactory<string, ControlBase>.Maker<ButtonControl>("Button Control");


Within the same assembly this pattern works fine, however as with the C++ implementation it falls over when you place the code within a library and attempt to reference it: the static objects don't get created!

Is there method I can use of forcing these objects to create when they are within an assembly I am referencing?

Thanks,
Scott


I think you have some terminology here confused. Static members are loaded the first time you use a class. In order to use them from your referencing assembly, both the members and the class must be public.

In your library:

namespace SomeNamespace
{
public class Makers
{
public static PluggableFactory<string, ControlBase>.Maker<ButtonControl> Maker = new PluggableFactory<string, ControlBase>.Maker<ButtonControl>("Button Control");
}
}


Then in the project using the code:


public class Program
{
public static void Main()
{
SomeNamespace.Makers.Maker.DoWhatever();
}
}


Note that this is a direct answer to your question, if I'm even understanding what you're asking. I do however have a feeling that this isn't what you should be doing. What exactly are you trying to achieve here and what library are you creating?

Is there method I can use of forcing these objects to create when they are within an assembly I am referencing?


There is not. The C# style approach to this involves Attributes and reflection, not member creation shenanigans.
Then yes I have got confused.

The constructor of a maker will register its type with the a singleton instance of the pluggable factory, using the key passed into the constructor, the idea being that you need no explicit code to load the factory with the types, only to add the type into the project.



public class Singleton<T> where T : class
{
static Singleton()
{
}

public static readonly T Instance =
typeof(T).InvokeMember(typeof(T).Name,
BindingFlags.CreateInstance |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic,
null, null, null) as T;
}

public class PluggableFactory<TKeyType, TBase> : Singleton<Factory<TKeyType, TBase>>
{
public class Maker<TDerivedType> where TDerivedType : TBase
{
public Maker(TKeyType name)
{
PluggableFactory<TKeyType, TBase>.Instance.RegisterType<TDerivedType>(name);
}
}
}

public class Factory<TKeyType, TBase>
{
private Dictionary<TKeyType, Type> loadedTypes = new Dictionary<TKeyType, Type>();

public Dictionary<TKeyType, Type>.KeyCollection Keys
{
get { return loadedTypes.Keys; }
}

public void RegisterType<TDerived>(TKeyType name) where TDerived : TBase
{
if (loadedTypes.ContainsKey(name))
{
Logging.Logger.LogWarning("Attempted to add duplicate item to factory. " +
" Alias: " + name.ToString() +
"Current Type: " + loadedTypes[name].ToString() +
"New Type: " + typeof(TBase).ToString() + ". Request ignored.");
}
else
{
loadedTypes.Add(name, typeof(TDerived));
}
}

public TBase Create(TKeyType name, params object[] args)
{
return (TBase)Activator.CreateInstance(loadedTypes[name], args);
}

public bool TryCreate(out TBase outObj, TKeyType name, params object[] args)
{
try
{
outObj = (TBase)Activator.CreateInstance(loadedTypes[name], args);
return true;
}
catch(Exception e)
{
Logging.Logger.LogError("Factory failed to create type: " + name.ToString() + ". Error Message: " + e.Message);

outObj = default(TBase);
return false;
}
}
}



An example:

Listing 1


class Base {public void Print(){Console.WriteLine("Base");}

class A : Base {public void Print(){Console.WriteLine("A");}
class B : Base {public void Print(){Console.WriteLine("B");}

static PluggableFactory<string, Base>.Maker<A> maker = new PluggableFactory<string, Base>.Maker<A>("A");
static PluggableFactory<string, Base>.Maker<B> maker = new PluggableFactory<string, Base>.Maker<B>("B");



Listing 2


void Func()
{
// Factory is already loaded with types due to the static maker objects
Base a = PluggableFactory<string, Base>.Instance.Create("A");
Base b = PluggableFactory<string, Base>.Instance.Create("B");

a.Print();
b.Print();
}



If listing 1 is in the same assembly as listing 2 then the output is A B if listing 1 is in a different assembly to listing 2 (ie a common library) then PluggableFactory<string, Base> contains no types and fails during the calls to Create.

I hope that helped explain a little clearer. What I am after is for the constructor on the static Maker objects to be called automatically during program start up even when that static object is in a referenced library.
Game development blog and portfolio: http://gamexcore.co.uk

[quote name='gameXcore' timestamp='1307620946' post='4821273']
Is there method I can use of forcing these objects to create when they are within an assembly I am referencing?


There is not. The C# style approach to this involves Attributes and reflection, not member creation shenanigans.
[/quote]

Ahh that was posted during my last post, as you have probably gathered, in depth knowledge of C# is something I don't yet possess, could you give me a quick explanation of what this would involve?

My first idea is that I would attribute my classes with a tag of some kind and then write some code that checks through all the types in the assembly and adds them into the factory if they have the tag?
Game development blog and portfolio: http://gamexcore.co.uk

My first idea is that I would attribute my classes with a tag of some kind and then write some code that checks through all the types in the assembly and adds them into the factory if they have the tag?


That's exactly how the attributes / reflection system would work. Attributes are the "tag" and reflection is used to run through the list of types in an assembly. Writing your own attribute is easy; just derive from the Attribute class and then mark your types with it. For reflection, take a look at the Assembly class, specifically the GetTypes method.
Mike Popoloski | Journal | SlimDX
Indeed. If you're not just doing it as a learning exercise, then the Managed Extensibility Framework, or depending on what you use it for, a variety of IoC containers already exist for your consumption.

This topic is closed to new replies.

Advertisement