Teddy's Blog

- Sharing my ideas...

<July 2008>
SuMoTuWeThFrSa
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789


Navigation

Links

Subscriptions

News



High performance non-generic wrapper for generic class and method

Attachments

Download related source code of this post

 

Background

Some time, generic is an elegant and useful tool but never all the time

* When you feel generic programming is so attractive. And you are trying

to write more and more your code as generic, please slow down a little,

and do some thinking.

- Do you really need so many generic classes/methods?

- Are you trapping yourself?

- Or, at least, have you consider writing some non-generic wrappers for

your generic components?

 

Why

Why you need a non-generic wrapper for generic code?

* Your writing component depends on calling another generic component,

but you don't want to publish your component as generic or you want to

publish your component with different generic parameter types from the

called component. And you don't want to lose much performance.

 

How

** Class initialization and method calling by Reflection generally is performance

killer, so I'll not provide any reflection solutions here.

Case 1: Generic class initialization

* Background:

You are writing class B calling generic class A, and you want to initialize

an instance of A.

class A
{
	public void Foo()
	{
		//do something according to T
	}
}
class B
{
	public void Foo(Type type)
	{
		//How to call new A().Foo() using type as  here
		//...
	}
}

* Solution:

Convert a generic class initialization problem to a generic method calling problem.

class B
{
	public void Foo(Type type)
	{
		//Please go to Case 2 to see how to call B.Foo() using type as  here
		//...
	}
	public void Foo()
	{
		new A().Foo();
	}
}

Case 2: Simple generic method calling

* Background:

You are writing class B's non-generic method Foo(Type type) calling a generic method

Foo().

class B
{
	public void Foo(Type type)
	{
		//How to call B.Foo() using type as  here
		//...
	}
	public void Foo()
	{
		//do something according to T
	}
}

* Solution:

- If 's possible value can be known at design time, we can use some if-else.

public void Foo(Type type)
{
	if (type == typeof(A))
	{
		Foo();
	}
	else ...
}

- Or, if 's possible value can only be known at runtime, or, although they can be known

at design time, but the amount of types are impossible to be enumerated up, we can try to

use DynamicMethod to emit non-reflection generic method calling code, because at the

time you construct the DynamicMethod, you should always know what the type parameter is.

Use DynamicMethodFactory class encapsulating this kind of DynamicMethod construction

which can return a delegate for the DynamicMethod. (The source code of this component

is attached with the sample code. Please see more info of this component in the reference

section.)

public void Foo(Type type)
{
	//find the generic method you want to call
	MethodInfo mi = typeof(B).GetMethod("Foo", Type.EmptyTypes);
	//create the delegate by method info and generic parameter type
	DynamicMethodProxyHandler handler = new DynamicMethodFactory().GetMethodDelegate(mi, type);
	//invoke the method through delegate
	handler(this, null);
}

You can see, this solution is general and the performance is similar to invoke Foo() directly.

The details of the DynamicMethod ILs emitted by the factory class is just like "invoke Foo()

directly".

This is a very simple generic method, so now I guess you are looking forward to more complex ones?

Let's go on! BTW, how complex to you want? :)

Case 3: Complex generic method calling

* Background:

Now in non-generic class D, let's wrap the calling to a very complex generic method ComplexFoo() of

Class C, ComplexFoo() has input, ref, output parameters and return value. And it has two generic

parameter types.

class C
{
	public string ComplexFoo<T1, T2>(string inputStr, ref int refInt, out int outInt)
	{
		refInt++;
		string retStr = string.Format("{0}, {1}, {2}", typeof(T1), typeof(T2), refInt);
		outInt = retStr.Length;
		return retStr;
	}
}
class D
{
	public string ComplexFoo(Type t1, Type t2, string inputStr, ref int refInt, out int outInt)
	{
		//How to call new C().ComplexFoo<T1, T2>(string inputStr, ref int refInt, out int outInt)?
		//...
	}
}

* Solution:

- Use DynamicMethodFactory.

class D
{
	public string ComplexFoo(Type t1, Type t2, string inputStr, ref int refInt, out int outInt)
	{
		//find the generic method you want to invoke
		MethodInfo mi = typeof(C).GetMethod("ComplexFoo");
		//create the delegate by method info and generic parameter types
		DynamicMethodProxyHandler handler = new DynamicMethodFactory().GetMethodDelegate(mi, t1, t2);
		//invoke the method through delegate
		object[] paramValues = new object[3];
		paramValues[0] = inputStr;
		paramValues[1] = refInt;
		//paramValues[2] is null to contain the out parameter value
		string retStr = (string)handler(this, paramValues);
		refInt = (int)paramValues[1]; //get the ref parameter value after invoke
		outInt = (int)paramValues[2]; //get the output parameter value after invoke
		return retStr;
	}
}

As you can see, the DynamicMethod delegate created by the factory class supports all kinds of method

parameter types and any number of generic parameter types.

 

Do you think it is useful?

 

References:

* What is NBear.Common, where comes it?

The NBear.Common library here is one of the common components of an open source project - NBear's version 4,

which is under development now.

Actually, the sample here only demonstrates a piece of the library, even hasn't demonstrated all functions

of the DynamicMethodFactory class.

You can go into the source code and find "UnitTest" section for more introductions for other functions.

I'll introduce more other interesting things related to this library and the NBear project in the further.

 

Hope you love it! Looking forward to your ideas to make it better.

posted Saturday, November 24, 2007 7:36 AM by teddy with 247 Comments

The new beginning!
Today, I cleared all old data in this blog. It is a new beginning! I'll continue sharing my ideas here with you all...

posted Friday, November 23, 2007 10:01 PM by teddy with 0 Comments




Powered by Dot Net Junkies, by Telligent Systems