473,385 Members | 1,409 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Instantiating types (with internal constructors) using a generic factory

Hello!

Whilst refactoring an application, I was looking at optimizing a
ModelFactory with generics. Unfortunately, the business objects created by
the ModelFactory doesn't provide public constructors (because we do not
allow developers to instantiate them directly).

Because our business objects are instantiated very frequently, the idea of
using reflection sounds like a performance killer (I haven't done any tests
on this, but the documentation suggests that Activator.CreateInstance() is
slower than newing up objects directly).

The goal is to have a central access point for creating models (hence the
use of a factory), so that all objects are guaranteed a common state before
being handed over to other tiers.

The following sample illustrates the desired behaviour:

public abstract class CmsObjectNode
{
internal CmsObjectNode(CmsContext c) {}
}

public sealed class Page : CmsObjectNode
{
internal Page(CmsContext c) : base(c) { }
}

// User
// Role
// etc.

static class ModelFactory
{
//
public static Page CreatePage(CmsContext c)
{
return new Page(c);
}

// CreateUser
// CreateRole
// etc.

public static T CreateCmsObjectNode<T>(CmsContext c) where T :
CmsObjectNode
{
return new T(c);
}
}

...

I then thought: "Perhaps I could put the factory method on each business
type instead (just for testing purposes), and have the factory use that ..".
The following sample illustrates the factory method implementation:

public abstract class CmsObjectNode
{
internal CmsObjectNode(CmsContext c) {}
internal abstract T Create<T>(CmsContext c) where T : CmsObjectNode;
}

public sealed class Page : CmsObjectNode
{
internal Page(CmsContext c) : base(c) { }
override internal Page Create<Page>(CmsContext c)
{
return new T(c);
}
}

// User
// Role
// etc.

static class ModelFactory
{
//
public static Page CreatePage(CmsContext c)
{
return new Page(c);
}

// CreateUser
// CreateRole
// etc.

public static T CreateCmsObjectNode<T>(CmsContext c) where T :
CmsObjectNode
{
return new T(c);
}
}

I see two solutions to this problem:

1. Keep the current ModelFactory with concrete methods (although I don't
like the redundance pattern).
2. Replace requests to the ModelFactory with request to each type instead.
This provides a single access point for creating types - and the factory
method would reside on the concrete implementation.

I feel that models and factories should be seperated, but I can't see a
solution to the above.

Thanks in advance!

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)
Jan 5 '06 #1
5 4550
Anders,

Well, I think the first thing you are going to realize is that you won't
be able to do:

new T(c);

Since the base class constructors are not automatically exposed, it
should give you a compile-time error.

I don't know that there is a good solution to this. You could use an
interface which is internal, which all the classes implement. This would be
something like ICmsObjectNode with a method called Initialize, which takes
your context, and initializes the object.

Unfortunately, this will result in you having to make the constructor of
your classes public, so you can use the new constraint. So that's not a
viable option.

You might have to ultimately rely on reflection to do this. Either
that, or create a switch statement that you have to maintain (any solution
that doesn't use reflection is ultimately going to require some specialized
code which has to be maintained when new types are created).

Hope this helps.
--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com


"Anders Borum" <an****@sphereworks.dk> wrote in message
news:eH***************@TK2MSFTNGP09.phx.gbl...
Hello!

Whilst refactoring an application, I was looking at optimizing a
ModelFactory with generics. Unfortunately, the business objects created by
the ModelFactory doesn't provide public constructors (because we do not
allow developers to instantiate them directly).

Because our business objects are instantiated very frequently, the idea of
using reflection sounds like a performance killer (I haven't done any
tests on this, but the documentation suggests that
Activator.CreateInstance() is slower than newing up objects directly).

The goal is to have a central access point for creating models (hence the
use of a factory), so that all objects are guaranteed a common state
before being handed over to other tiers.

The following sample illustrates the desired behaviour:

public abstract class CmsObjectNode
{
internal CmsObjectNode(CmsContext c) {}
}

public sealed class Page : CmsObjectNode
{
internal Page(CmsContext c) : base(c) { }
}

// User
// Role
// etc.

static class ModelFactory
{
//
public static Page CreatePage(CmsContext c)
{
return new Page(c);
}

// CreateUser
// CreateRole
// etc.

public static T CreateCmsObjectNode<T>(CmsContext c) where T :
CmsObjectNode
{
return new T(c);
}
}

..

I then thought: "Perhaps I could put the factory method on each business
type instead (just for testing purposes), and have the factory use that
..". The following sample illustrates the factory method implementation:

public abstract class CmsObjectNode
{
internal CmsObjectNode(CmsContext c) {}
internal abstract T Create<T>(CmsContext c) where T : CmsObjectNode;
}

public sealed class Page : CmsObjectNode
{
internal Page(CmsContext c) : base(c) { }
override internal Page Create<Page>(CmsContext c)
{
return new T(c);
}
}

// User
// Role
// etc.

static class ModelFactory
{
//
public static Page CreatePage(CmsContext c)
{
return new Page(c);
}

// CreateUser
// CreateRole
// etc.

public static T CreateCmsObjectNode<T>(CmsContext c) where T :
CmsObjectNode
{
return new T(c);
}
}

I see two solutions to this problem:

1. Keep the current ModelFactory with concrete methods (although I don't
like the redundance pattern).
2. Replace requests to the ModelFactory with request to each type instead.
This provides a single access point for creating types - and the factory
method would reside on the concrete implementation.

I feel that models and factories should be seperated, but I can't see a
solution to the above.

Thanks in advance!

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)

Jan 5 '06 #2
Nicholas, thanks for the reply.
Since the base class constructors are not automatically exposed, it
should give you a compile-time error.
It does; I was simply posting the sample code to illustrate my findings.
I don't know that there is a good solution to this. You could use an
interface which is internal, which all the classes implement. This would
be something like ICmsObjectNode with a method called Initialize, which
takes your context, and initializes the object.
The goal of the ModelFactory is providing a single access point for newing
up my business objects. The initialization (or object population) process is
controlled by another part of the framework. Exposing an initialization
method using an internal interface is one way to do it, but I am simply
looking for the best solution for newing up my business objects ..

I guess this boils down to a more architectural question; it is an accepted
practice to let business objects be responsible for newing up instances of
their own type?

I see all the different implementations, but am looking for best practices.
Unfortunately, this will result in you having to make the constructor
of your classes public, so you can use the new constraint. So that's not
a viable option.
Unfortunately not. It is not up the user of the API to create new instances,
as all business objects (from a public point of view) are created in context
of another object.

Page parent = CmsHttpContext.GetPage(..);
Page child = parent.Create(..);
You might have to ultimately rely on reflection to do this. Either
that, or create a switch statement that you have to maintain (any solution
that doesn't use reflection is ultimately going to require some
specialized code which has to be maintained when new types are created).


The number of models (or types in question) in the application are approx.
15, and I have no plans on expand that number at the moment. Just for the
fun of it, I'll do a performance test using reflection (things are not
always what they seem).

Regarding the switch-statement; would you use the type of the object?

Page created = ModelFactory.Create(typeof(Page));

Again, I haven't been measuring performance but I believe reflection
operations are quite expensive. Lots of business objects have to be
instantiated all the time, so this is an absolutely crucial part of the
application.

Thanks for reading so far :)

With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)
Jan 5 '06 #3
I did the performance test yesterday after reading your post; by my figures,
reflection takes 80 times as long, but that still allows you to create a
good 100,000 objects in a second... This is using a generic Factory<T>
class, which (in the static constructor) pre-caches the T(Context c) ctor in
a private static field (to avoid lots of calls to GetConstructor).

Full code follows (maybe could be done better); I also get the public ctor
taking significantly less time than the others... I haven't switched the
orders to confirm it, but I suspect this is because it is the first one, and
I am filling up memory...

Marc

public class Program {
public static void Main() {
Context c = new Context();
Test<Test1>(c); // public
Test<Test2>(c); // private
Test<Test3>(c); // internal
Test<Test4>(c); // no such ctor
Test(c); // non-generic
Console.ReadLine();
}
private const int COUNT = 100000;
private static void Test<T>(Context c) { // test function;
instatiate a good few T's (using generics / reflection)
string name = typeof(T).Name;
try {
DateTime start = DateTime.Now;
for (int i = 0; i < COUNT; i++) {
Factory<T>.Create(c);
}
DateTime end = DateTime.Now;
Console.WriteLine("{0}: {1}", name,
end.Subtract(start).TotalMilliseconds);
GC.Collect(); // since throwing away lots of objects... not
recommended in production code
} catch (Exception e) {
Console.WriteLine("{0}: {1}",name,e.GetType().Name);
}
}
private static void Test(Context c) {// test function; instatiate a
good few T's (using ctor directly)
string name = "Test3*";
try {
DateTime start = DateTime.Now;
for (int i = 0; i < COUNT; i++) {
new Test3(c);
}
DateTime end = DateTime.Now;
Console.WriteLine("{0}: {1}", name,
end.Subtract(start).TotalMilliseconds);
GC.Collect(); // since throwing away lots of objects... not
recommended in production code
} catch (Exception e) {
Console.WriteLine("{0}: {1}", name, e.GetType().Name);
}
}
}
public class Test1 {public Test1(Context c) { }}
public class Test2 {private Test2(Context c) { }}
public class Test3 {internal Test3(Context c) { }}
public class Test4 {}

public class Context {}
public static class Factory<T> {

private static System.Reflection.ConstructorInfo _ctor;
static Factory() {
const System.Reflection.BindingFlags flags =
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance;
_ctor = typeof(T).GetConstructor(flags,null, new Type[] {
typeof(Context) },null);
if (_ctor == null) throw new NotSupportedException();
}
public static T Create(Context c) {
return (T) _ctor.Invoke(new object[] { c });
}
}

"Anders Borum" <an****@sphereworks.dk> wrote in message
news:eA*************@tk2msftngp13.phx.gbl...
Nicholas, thanks for the reply.
Since the base class constructors are not automatically exposed, it
should give you a compile-time error.


It does; I was simply posting the sample code to illustrate my findings.
I don't know that there is a good solution to this. You could use an
interface which is internal, which all the classes implement. This would
be something like ICmsObjectNode with a method called Initialize, which
takes your context, and initializes the object.


The goal of the ModelFactory is providing a single access point for newing
up my business objects. The initialization (or object population) process
is controlled by another part of the framework. Exposing an initialization
method using an internal interface is one way to do it, but I am simply
looking for the best solution for newing up my business objects ..

I guess this boils down to a more architectural question; it is an
accepted practice to let business objects be responsible for newing up
instances of their own type?

I see all the different implementations, but am looking for best
practices.
Unfortunately, this will result in you having to make the constructor
of your classes public, so you can use the new constraint. So that's not
a viable option.


Unfortunately not. It is not up the user of the API to create new
instances, as all business objects (from a public point of view) are
created in context of another object.

Page parent = CmsHttpContext.GetPage(..);
Page child = parent.Create(..);
You might have to ultimately rely on reflection to do this. Either
that, or create a switch statement that you have to maintain (any
solution that doesn't use reflection is ultimately going to require some
specialized code which has to be maintained when new types are created).


The number of models (or types in question) in the application are approx.
15, and I have no plans on expand that number at the moment. Just for the
fun of it, I'll do a performance test using reflection (things are not
always what they seem).

Regarding the switch-statement; would you use the type of the object?

Page created = ModelFactory.Create(typeof(Page));

Again, I haven't been measuring performance but I believe reflection
operations are quite expensive. Lots of business objects have to be
instantiated all the time, so this is an absolutely crucial part of the
application.

Thanks for reading so far :)

With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)

Jan 6 '06 #4
"Anders Borum" <an****@sphereworks.dk> a écrit dans le message de news:
eH***************@TK2MSFTNGP09.phx.gbl...

| I see two solutions to this problem:

| 2. Replace requests to the ModelFactory with request to each type instead.
| This provides a single access point for creating types - and the factory
| method would reside on the concrete implementation.

Using the static method that takes a parameter to create an instance is just
the same as adding a parameterised constructor. Provided you don't provide a
default "noparams" constructor, then that parameterised constructor becomes
the only means to instantiate that class.

| 1. Keep the current ModelFactory with concrete methods (although I don't
| like the redundance pattern).

Unfortunately, unlike Delphi, C# doesn't have the concept of virtual
constructors and your problem is one that I have had to cope with due to
that lack of virtual constructors.

My first reaction was to design the metaclass equivalent of the Delphi
"class of" construct. In Delphi, you could do the following :

type
CmsObjectNode = class
...
public
constructor Create(c: CmsContext); virtual;
end;

CmsObjectNodeClass = class of CmsObjectNode;

Page = class(CmsObjectNode)
...
public
constructor Create(c: CmsContext); override;
end;

Then in calling code you could do this :

var
NodeClass: CmsObjectNodeClass;

Node: CmsObjectNode;
begin
NodeClass := Page;

Node := NodeClass.Create(context); // create a Page

NodeClass := OtherDerivedNode;

Node := NodeClass.Create(context); // create an OtherDerivedNode

In C#, I created a "class of" class or metaclass like this :

class MetaClass
{
private Type fClassType;

public Class(Type aType)
{
fClassType = aType;
}

public Type ClassType
{
get
{
return fClassType;
}
set
{
if (!(value.Equals(fClassType) || value.IsSubclassOf(fClassType)))
throw new TypeNotDerivedException(value, fClassType);
fClassType = value;
}
}
}

public object Create(object[] args)
{
Type[] argTypes = new Type[args.Length];

for (int i = 0; i < args.Length; i++)
argTypes[i] = args[i].GetType();

ConstructorInfo lConstructor =
fClassType.GetConstructor(BindingFlags.Instance |
BindingFlags.NonPublic|
BindingFlags.Public,
null, argTypes, null);
if (lConstructor == null)
throw new ConstructorNotFoundException();

return lConstructor.Invoke(args);
}
}

This gets used with a hierarchy like this :

public abstract class Node
{
...
}

class NodeClass : Class
{
public NodeClass() : base(typeof(Node)) {}

public Node Create()
{
return Create(new object[0]);
}

public Node Create(object[] args)
{
return (Node) base.Create(args);
}

public static implicit operator NodeClass(Type type)
{
NodeClass result = new NodeClass();
result.ClassType = type;
return result;
}
}

class Page : Node
{
...
}

class Paragraph : Node
{
...
}

And the calling code does the following :

private void button1_Click(object sender, System.EventArgs e)
{
NodeClass nodeClass = typeof(Page); // create Page

Node node = pageClass.Create();

nodeClass = typeof(Paragraph); // create Paragraph

node = nodeClass.Create();
}

You can also write other parameterised constructors for the base class and
pass the expected parameters to the Create(object[] args) method.

Now the above might not be exactly what you want, but it does avoid using
Activator.CreateInstance(...) and that might help bring down the execution
time. You could also use the same ConstructorInfo technique in your factory
if you don't like the metaclass way of doing things.

| I feel that models and factories should be seperated, but I can't see a
| solution to the above.
In my experience, factories are reasonably intimately tied in to the classes
that they create.
I don't see too much problem with your internal linking of your factory with
your classes' constructors, apart from the code redundancy; What do you
think of my option ?

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Jan 6 '06 #5
Marc,

You beat me to the point with the performance test - thanks for providing
the sample; it provides data for choosing the best implementation. As
previously mentioned, object initialization (or newing) is a core part of
the framework so a penaulty of ~80 times longer processing per instance is
not the right solution here.

That said other applications that are not as intensive in terms of newing
may never notice the degrade (and it does provide for a cleaner solution).
Instead of hacing

The approach provided by Joanna is also very interesting. I guess the idea
of a centralized factory is a sound architectural decision, but its simplicy
and obvious redundant patterns asks for another solution (which is why I was
looking to delegate the creation to the types instead, possibly with a
similar approach as provided by Joanna).

This is the output of your test (in ms):

Test1: 187,5
Test2: 796,875
Test3: 625
Test4: TypeInitializationException
Test3*: 0
Full code follows (maybe could be done better); I also get the public ctor
taking significantly less time than the others... I haven't switched the
orders to confirm it, but I suspect this is because it is the first one,
and I am filling up memory...


Tried switching with no effect.

[ code clipped ]

With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)
Jan 6 '06 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: svilen | last post by:
hello again. i'm now into using python instead of another language(s) for describing structures of data, including names, structure, type-checks, conversions, value-validations, metadata etc....
3
by: Sathyaish | last post by:
What is a private constructor, and why would a class have one? What are the other kinds of constructors besides: (1) public constructors; and (2) parameterized constructors And I understand...
6
by: BBM | last post by:
I have an object that has a fairly complex construction sequence, so I have written a dedicated "factory" class that invokes the constructor of my object class (which does nothing but instantiate...
2
by: david | last post by:
Well, as a matter of fact I_HAD_MISSED a basic thing or two, anyway, although Ollie's answer makes perfectly sense when dealing with classes, it doesn't seem to me to apply as well if you have to...
12
by: Edward Diener | last post by:
Given value class X { public: // Not allowed: X():i(100000),s(10000) { } // Allowed void InitializeDefaults() { i = 100000; s = 10000; } private: int i;
2
by: gilbert | last post by:
Hello. I am trying to use c# generic to define a class. class MyClass<Twhere T: new(){ } In this definition, MyClass can create T objects with a default constructor. Is there any way to...
2
by: H.S. | last post by:
Hello, I have written a numerical library and currently the constructor assumes that the input data is of a particular type (data is 2D array). I would like to add the functionality that the...
9
by: VK | last post by:
<OT>I am finishing TransModal 0.1 so planning to move it from alpha to beta stage.<OT> Besides that I am planning to write an introductory to inheritance schema currently used in Javascript...
9
by: Simon Woods | last post by:
Hi I have a factory class which I want to use to create new objects which inherit a certain base type so I have a declaration Public Class MyOwnFactory(Of T As MyOwnBase) As I understand...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.