看了 Kanas.Net 的 以非泛型方式调用泛型方法 ,思考了一下午。
(一)简化
Kanas.Net 对比的5种方案中,1、2、5 三种方案均需要在编译时指定所使用的类型,因此并未解决问题。方案3性能较低,方案4是有效方案:
·定义泛型委托;
·定义非泛型接口;
·实现这个接口;
·通过泛型委托获取非泛型接口的实现。
感觉方案4将问题复杂化了,这里采用委托用处不大,平添加许多复杂性。
我先前错误认为.net中的范型方法是在运行时才实例化的,装配脑袋 指出是在编译期实例化的,用reflector查看代码,正如装配脑袋所言,范型方法是在编译器实例化。因此,要解决以非范型方式调用范型方法的关键就在于在运行期产生指定类型范型方法。Kanas.Net 的方案4既是这个策略:
(1)申明一个非范型接口,用来持有运行期生成的方法,供后期调用;
(2)采用一个范型类,继承非范型接口
(3)一个方法,能够根据指定的类型名称,创建相应的范型类。
采用这种策略,更简洁的实现是引入helper:
1 public interface IServerClassHelper
2 {
3 void InvokeAdd(object obj, object list);
4 }
5
6 public class ServerClass
7 {
8 public void Add<T>(T obj, ICollection<T> c)
9 {
10 c.Add(obj);
11 }
12
13 public IServerClassHelper CreateHelper(Type T)
14 {
15 Type helperType = typeof(ServerClassHelper<>).MakeGenericType(T);
16 IServerClassHelper helper = Activator.CreateInstance(helperType, this) as IServerClassHelper;
17 return helper;
18 }
19
20 private sealed class ServerClassHelper<T> : IServerClassHelper
21 {
22 private ServerClass m_server;
23
24 public ServerClassHelper(ServerClass server)
25 {
26 m_server = server;
27 }
28
29 public void InvokeAdd(object obj, object list)
30 {
31 m_server.Add<T>((T)obj, (ICollection<T>)list);
32 }
33 }
34 }
2 {
3 void InvokeAdd(object obj, object list);
4 }
5
6 public class ServerClass
7 {
8 public void Add<T>(T obj, ICollection<T> c)
9 {
10 c.Add(obj);
11 }
12
13 public IServerClassHelper CreateHelper(Type T)
14 {
15 Type helperType = typeof(ServerClassHelper<>).MakeGenericType(T);
16 IServerClassHelper helper = Activator.CreateInstance(helperType, this) as IServerClassHelper;
17 return helper;
18 }
19
20 private sealed class ServerClassHelper<T> : IServerClassHelper
21 {
22 private ServerClass m_server;
23
24 public ServerClassHelper(ServerClass server)
25 {
26 m_server = server;
27 }
28
29 public void InvokeAdd(object obj, object list)
30 {
31 m_server.Add<T>((T)obj, (ICollection<T>)list);
32 }
33 }
34 }
使用:
1 ClientClassA a = new ClientClassA();
2 ICollection<ClientClassA> ca = new List<ClientClassA>();
3 String TypeString = "GenericMethodTest.ClientClassA";
4 IServerClassHelper helper = server.CreateHelper(Type.GetType(TypeString));
5 helper.InvokeAdd(a, ca);
2 ICollection<ClientClassA> ca = new List<ClientClassA>();
3 String TypeString = "GenericMethodTest.ClientClassA";
4 IServerClassHelper helper = server.CreateHelper(Type.GetType(TypeString));
5 helper.InvokeAdd(a, ca);
性能测试:
1 : 2.09
(二)改进
上面方案使用起来还是比较复杂,首先需要获得一个helper,然后调用helper的相应方法。还是不够直接。继续改进吧,改进成<以非泛型方式调用泛型方法>一文开始所述接口:
Add(Type type, Object obj, Object c)。
以下是代码:
1 public class ServerClass
2 {
3 private static ICollection<IServerClassHelper> m_helpers = new List<IServerClassHelper>();
4
5 public void Add<T>(T obj, ICollection<T> c)
6 {
7 c.Add(obj);
8 }
9
10 public void Add(Type type, Object obj, Object c)
11 {
12 GetHelper(type).InvokeAdd(obj, c);
13 }
14
15 private IServerClassHelper GetHelper(Type T)
16 {
17 foreach(IServerClassHelper h in m_helpers)
18 {
19 if (h.Equals(T)) return h;
20 }
21 IServerClassHelper helper = CreateHelper(T);
22 m_helpers.Add(helper);
23 return helper;
24 }
25
26 private IServerClassHelper CreateHelper(Type T)
27 {
28 Type clientType = typeof(ServerClassHelper<>).MakeGenericType(T);
29 IServerClassHelper helper = Activator.CreateInstance(clientType, this, T) as IServerClassHelper;
30 return helper;
31 }
32
33 private interface IServerClassHelper : IEquatable<Type>
34 {
35 void InvokeAdd(object obj, object list);
36 }
37
38 private sealed class ServerClassHelper<T> : IServerClassHelper
39 {
40 private ServerClass m_server;
41 private Type m_type;
42
43 public ServerClassHelper(ServerClass server, Type type)
44 {
45 if (server == null) throw new ArgumentNullException();
46 if (type == null) throw new ArgumentNullException();
47 m_server = server;
48 m_type = type;
49 }
50
51 public void InvokeAdd(object obj, object list)
52 {
53 m_server.Add<T>((T)obj, (ICollection<T>)list);
54 }
55
56 IEquatable 成员
65 }
66 }
2 {
3 private static ICollection<IServerClassHelper> m_helpers = new List<IServerClassHelper>();
4
5 public void Add<T>(T obj, ICollection<T> c)
6 {
7 c.Add(obj);
8 }
9
10 public void Add(Type type, Object obj, Object c)
11 {
12 GetHelper(type).InvokeAdd(obj, c);
13 }
14
15 private IServerClassHelper GetHelper(Type T)
16 {
17 foreach(IServerClassHelper h in m_helpers)
18 {
19 if (h.Equals(T)) return h;
20 }
21 IServerClassHelper helper = CreateHelper(T);
22 m_helpers.Add(helper);
23 return helper;
24 }
25
26 private IServerClassHelper CreateHelper(Type T)
27 {
28 Type clientType = typeof(ServerClassHelper<>).MakeGenericType(T);
29 IServerClassHelper helper = Activator.CreateInstance(clientType, this, T) as IServerClassHelper;
30 return helper;
31 }
32
33 private interface IServerClassHelper : IEquatable<Type>
34 {
35 void InvokeAdd(object obj, object list);
36 }
37
38 private sealed class ServerClassHelper<T> : IServerClassHelper
39 {
40 private ServerClass m_server;
41 private Type m_type;
42
43 public ServerClassHelper(ServerClass server, Type type)
44 {
45 if (server == null) throw new ArgumentNullException();
46 if (type == null) throw new ArgumentNullException();
47 m_server = server;
48 m_type = type;
49 }
50
51 public void InvokeAdd(object obj, object list)
52 {
53 m_server.Add<T>((T)obj, (ICollection<T>)list);
54 }
55
56 IEquatable
65 }
66 }
使用:
1 String TypeString = "GenericMethodTest.ClientClassA";
2 Type clientType = Type.GetType(TypeString);
3 server.Add(clientType, a, ca);
4
2 Type clientType = Type.GetType(TypeString);
3 server.Add(clientType, a, ca);
4
好了,这下简单了,复杂的都封装到ServerClass的内部了。
性能:
1 : 6
(三)消除问题
从以上实现可以看出,关键问题在于将范型方法映射为不带范型方法范型类,然后在运行期实例化。何必这么复杂呢,为什么不将 ServerClass申明为一个范型类呢?这样又存在另外一个问题,如何在运行期持有ServerClass<T>类对象呢?要解决这个问题,又需要申明非范型接口。
不过再仔细考虑,到底需不需要非泛型方式调用泛型方法?
将所需传入的类型叫做ClientClass,分析两种应用场景:
(1)ServerClass 知道 ClientClass 的存在,那么在编译时就可以进行推演。
(2)ServerClass 不知道 ClientClass 的存在,但是在内存中,总存在一种类型,ClientClass以这种类型被持有,而ServerClass又知道这种类型的存在,ServerClass可以以该类型为类型参数,在编译期就实例化。这种类型一般是ServerClass那边定义的接口。假如 ClientClass 不能实现该接口,包装一下子嘛!
所以我觉得,最好不要以非范型方式调用范型方法.