Decentralized unit initialization

A

Anton Shepelev

Hello all,

I have a project with a lot of relatively small
classes, currently static, all of which have a sin-
gle public method, Init(), that registers the class
in a central module by subscribing for an event.

With such an architecture, adding a new class re-
quires also adding a line to the central module to
call its Init() method, so I have there:

public static void InitAll()
{ A.Init();
B.Init();
C.Init();
//...
}

Is it somehow possible to remove this coupling, so
that it would be enough just to add a new class to
the project for it get automatically subscribed? I
have thought of static constructors, but they are
called in a lazy manner, at the moment of first ref-
erence, and are therefore useless in my situation.
 
M

Marcel Müller

Am 24.12.2013 09:04, schrieb Anton Shepelev:
I have a project with a lot of relatively small
classes, currently static, all of which have a sin-
gle public method, Init(), that registers the class
in a central module by subscribing for an event.

With such an architecture, adding a new class re-
quires also adding a line to the central module to
call its Init() method, so I have there:

public static void InitAll()
{ A.Init();
B.Init();
C.Init();
//...
}

Same here, but it is an ORM where A, B, C are entity types and I build
the entity type repository this way.
Is it somehow possible to remove this coupling, so
that it would be enough just to add a new class to
the project for it get automatically subscribed?

Yes, that is possible.

#1) Place the Init() code into the static constructor of A, B, C
respectively.

#2) Implement a tag interface and go by reflection through your
assemblies. Every class that implements this tag interface must have a
static Init method. This is then called from InitAll.
I
have thought of static constructors, but they are
called in a lazy manner, at the moment of first ref-
erence, and are therefore useless in my situation.

Same here. Then you need to go #2.
However you can combine #1 and #2 in several ways. First of all, the
common init method logically exposed by the tag interface could be the
static constructor itself. So the reflection code might simply force the
static constructor to run.
Secondly, the init might need to be a two stage process to avoid
initialization sequence problems. Stage one is the static constructor,
stage two is some Init function, that relies on the other types already
have passed stage one.


Marcel
 
A

Arne Vajhøj

I have a project with a lot of relatively small
classes, currently static, all of which have a sin-
gle public method, Init(), that registers the class
in a central module by subscribing for an event.

With such an architecture, adding a new class re-
quires also adding a line to the central module to
call its Init() method, so I have there:

public static void InitAll()
{ A.Init();
B.Init();
C.Init();
//...
}

Is it somehow possible to remove this coupling, so
that it would be enough just to add a new class to
the project for it get automatically subscribed? I
have thought of static constructors, but they are
called in a lazy manner, at the moment of first ref-
erence, and are therefore useless in my situation.

One obvious suggestion would be an attribute and
reflection.

Single assembly demo:

using System;
using System.Reflection;

namespace E
{
public class InitableAttribute : Attribute
{
}
[Initable]
public class A
{
public static void Init()
{
Console.WriteLine("Hello from A");
}
}
public class B
{
}
[Initable]
public class C
{
public static void Init()
{
Console.WriteLine("Hello from C");
}
}
public class D
{
}
[Initable]
public class E
{
public static void Init()
{
Console.WriteLine("Hello from E");
}
}
public class Program
{
public static void AllInit()
{
foreach(Type typ in
Assembly.GetExecutingAssembly().GetExportedTypes())
{
if(Attribute.GetCustomAttribute(typ,
typeof(InitableAttribute)) != null)
{
typ.InvokeMember("Init", BindingFlags.Public |
BindingFlags.Static | BindingFlags.InvokeMethod, null, null, new object[0]);
}
}
}
public static void Main(string[] args)
{
AllInit();
}
}
}

Arne
 
A

Arne Vajhøj

One obvious suggestion would be an attribute and
reflection.

Single assembly demo:

Based on some of your previous questions I somewhat
suspect that this could be plugins in separate assemblies.

Here are a demo for that context:

C:\Work\asmload>type FW.cs
using System;

namespace E
{
public class InitableAttribute : Attribute
{
}
}
C:\Work\asmload>csc /t:library FW.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\asmload>type P1.cs
using System;

namespace E
{
[Initable]
public class A
{
public static void Init()
{
Console.WriteLine("Hello from A");
}
}
public class B
{
}
[Initable]
public class C
{
public static void Init()
{
Console.WriteLine("Hello from C");
}
}
}
C:\Work\asmload>csc /t:library /r:FW.dll P1.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\asmload>type P2.cs
using System;
using System.Reflection;

namespace E
{
public class D
{
}
[Initable]
public class E
{
public static void Init()
{
Console.WriteLine("Hello from E");
}
}
}
C:\Work\asmload>csc /t:library /r:FW.dll P2.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\asmload>type Main.cs
using System;
using System.IO;
using System.Reflection;

namespace E
{
public class Program
{
public static void MyAssemblyLoadEventHandler(object sender,
AssemblyLoa
dEventArgs args) {
foreach(Type typ in args.LoadedAssembly.GetExportedTypes())
{
if(Attribute.GetCustomAttribute(typ,
typeof(InitableAttribute))
!= null)
{
typ.InvokeMember("Init", BindingFlags.Public |
BindingFlags.
Static | BindingFlags.InvokeMethod, null, null, new object[0]);
}
}
}
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyLoad += new
AssemblyLoadEventHandler(MyAssemblyLoadEventHandler);
foreach(FileInfo fi in (new
DirectoryInfo(".")).GetFiles("P*.dll"))
{
Assembly.Load(Path.GetFileNameWithoutExtension(fi.Name));
}
}
}
}
C:\Work\asmload>csc /r:FW.dll Main.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\asmload>Main
Hello from A
Hello from C
Hello from E

Arne
 
A

Arne Vajhøj

Am 24.12.2013 09:04, schrieb Anton Shepelev:
#2) Implement a tag interface and go by reflection through your
assemblies. Every class that implements this tag interface must have a
static Init method. This is then called from InitAll.

For a case like this then I would prefer an attribute
over a tag interface.
Secondly, the init might need to be a two stage process to avoid
initialization sequence problems. Stage one is the static constructor,
stage two is some Init function, that relies on the other types already
have passed stage one.

That is a good point.

If there has to be a specific init order, then it could be tricky.

Maybe an attribute with a priority property?

Arne
 
M

Marcel Müller

For a case like this then I would prefer an attribute
over a tag interface.

OK, that's true. But as soon as you want to provide some extension
functions tat make only sense for classes that belong to that concept, a
tag interface is superior.
That is a good point.

If there has to be a specific init order, then it could be tricky.

Maybe an attribute with a priority property?

Maintaining priorities distributed over the code is mostly pain. You
might add another assembly that conflicts with existing init orders. I
had a similar problem with a distributed transaction manager shortly,
where different providers are executed in a special sequence that takes
care of the probability of the provider to fail in phase 2 of 2PC.

If you really rely on the init order, then I would prefer the manual
approach with the InitAll function that calls the initializers in the
right sequence. With an attribute you won't reasonably come over a bunch
of predefined initialization stages, from which each class can chose.


Marcel
 
A

Arne Vajhøj

OK, that's true. But as soon as you want to provide some extension
functions tat make only sense for classes that belong to that concept, a
tag interface is superior.

I am a bit skeptical about extension methods on interfaces with no
methods.

But I would certainly recommend interfaces with methods.
Maintaining priorities distributed over the code is mostly pain. You
might add another assembly that conflicts with existing init orders. I
had a similar problem with a distributed transaction manager shortly,
where different providers are executed in a special sequence that takes
care of the probability of the provider to fail in phase 2 of 2PC.

If you really rely on the init order, then I would prefer the manual
approach with the InitAll function that calls the initializers in the
right sequence. With an attribute you won't reasonably come over a bunch
of predefined initialization stages, from which each class can chose.

A plugin author will know what plugins it depend on and be able to
pick something that run later.

But yes it could get messy if a dependency was added later.

Arne
 
A

Anton Shepelev

Thank you for your feedback, Marcel and Arne.

Marcel Muller:
Arne Vajhoj:

OK, that's true. But as soon as you want to pro-
vide some extension functions tat make only sense
for classes that belong to that concept, a tag in-
terface is superior.

Do you prefer an attribute because my classes are
static and cannot implement an interface? Other-
wise, an interface would be better IMO because it
would guarantee the existence of an Init() method.
I could of course have an instance member implement
that interface and call the static Init() method
from there...
 
M

Marcel Müller

Am 27.12.2013 09:30, schrieb Anton Shepelev:
Do you prefer an attribute because my classes are
static and cannot implement an interface?

OK, static classes can't use interfaces.
Other-
wise, an interface would be better IMO because it
would guarantee the existence of an Init() method.

No, interfaces cannot declare static methods, although I often miss thit
feature in conjunction with generics.
I could of course have an instance member implement
that interface and call the static Init() method
from there...

This won't bring you any benefit.


Marcel
 
A

Anton Shepelev

Arne Vajhoj:
Based on some of your previous questions I some-
what suspect that this could be plugins in sepa-
rate assemblies.

Here are a demo for that context:
[...]

Many thanks for the examples, Arne. The first one
answers my quesiton. My plugins implement an inter-
face and instances are created using Assembly.
LoadFrom() and Assembly.CreateInstance(), while the
current question is about the internal architecture
of these plugins :)
 
A

Anton Shepelev

Marcel Muller to Anton Shepelev:
This won't bring you any benefit.

Well, it will save me from trying to call an non-ex-
istant method, although it is cumbersome, and
switching to dynamic classes is another option.
 
A

Arne Vajhøj

Marcel Muller:

Do you prefer an attribute because my classes are
static and cannot implement an interface? Other-
wise, an interface would be better IMO because it
would guarantee the existence of an Init() method.
I could of course have an instance member implement
that interface and call the static Init() method
from there...

That would be preferably as it avoids the need for reflection.

Arne
 

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