Sign in to follow this  
gameXcore

C# - Forcing static symbols within a library to load

Recommended Posts

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:

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

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

Share this post


Link to post
Share on other sites
[quote name='gameXcore' timestamp='1307620946' post='4821273']
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:

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

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
[/quote]

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:
[code]
namespace SomeNamespace
{
public class Makers
{
public static PluggableFactory<string, ControlBase>.Maker<ButtonControl> Maker = new PluggableFactory<string, ControlBase>.Maker<ButtonControl>("Button Control");
}
}
[/code]

Then in the project using the code:

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

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?

Share this post


Link to post
Share on other sites
[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?
[/quote]

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

Share this post


Link to post
Share on other sites
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.

[code]

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;
}
}
}

[/code]

An example:

Listing 1
[code]

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");

[/code]

Listing 2
[code]

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();
}

[/code]

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.

Share this post


Link to post
Share on other sites
[quote name='Telastyn' timestamp='1307631934' post='4821333']
[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?
[/quote]

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?

Share this post


Link to post
Share on other sites
[quote name='gameXcore' timestamp='1307632418' post='4821339']
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?
[/quote]

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 [url="http://msdn.microsoft.com/en-us/library/system.attribute.aspx"]Attribute[/url] class and then mark your types with it. For reflection, take a look at the [url="http://msdn.microsoft.com/en-us/library/system.reflection.assembly.aspx"]Assembly[/url] class, specifically the [url="http://msdn.microsoft.com/en-us/library/system.reflection.assembly.gettypes.aspx"]GetTypes[/url] method.

Share this post


Link to post
Share on other sites
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.

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