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.