反射 与 泛型

本文探讨了Java语言中反射和泛型技术的结合使用,展示了如何利用泛型改进反射操作,包括运行期对象初始化、方法调用及数组创建等,并讨论了反射如何帮助获取泛型信息。
转载:[url]http://www.gd-emb.org/detail/id-33870.html[/url]
摘要:反射和泛型的功能都十分强大,将它们结合起来也特别有趣,本文试图就Java语言的反射和泛型的关系做一点探讨,用以抛砖引玉,引起大家对两种技术的兴趣!
  研究泛型与反射之间的关系非常有趣。
  我们知道,反射和泛型都是Java的一种动态技术。而不像继承和多态,是面向对象的技术。可以这么说,反射和泛型都像是为了弥补像继承和多态这些面向对象技术的不足而产生的。模式多是建立在面向对象技术基础上的,因而每种模式多多少少在动态性,或者说扩展性方面有些不足,我们就又结合了反射和泛型对模式进行一定的扩展,使它在动态性方面更符合我们的要求。
  在将这些技术结合起来的过程中,我们多多少少会想到将泛型和反射结合起来。这是非常有趣的话题:范型和反射都使得Java有了一定的扩展性,但同时都有它自己的不足,而将这两种技术结合起来,是不是又能解决各自的不足,使得它们在动态性上更上一层楼呢?
  正像前面所说的,泛型和反射可以相互促进,我们先来看看泛型是怎么帮助反射的。
  我们知道,使用反射的一个最大的烦恼就是应用反射以后得到的基本上都是Object类型的对象,这种对象要使用,需要我们进行强制类型转化。而我们更知道,泛型的功能之一就是消除我们使用强制类型转化。
1. 运行期内初始化对象
  运行期内初始化对象是我们最常用的反射功能,但是我们通过反射在运行期内得到的对象的类型通常是Object类型的,而这个对象又需要我们在使用的时候进行强制类型转化。现在,有了反射,可以使我们不需要做强制类型转化这个工作。
  假设我们已经有了两个类:

public class Cls1 {
public void do1() {
// TODO Auto-generated method stub
System.out.println("cls1...");
}
}
public class Cls2 {
public void do2() {
// TODO Auto-generated method stub
System.out.println("cls2...");
}
}

我们需要在运行期内初始化这两个对象,我们可以设计如下的初始化方法:

public class Factory{
public static <U extends Object>U getInstance(String clsName)
{
try
{
Class<?> cls = Class.forName(clsName);
return (U) cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}

在这个方法里,我们其实是利用泛型在初始化方法里提前做了强制类型转化的工作,这样使得我们不必在使用的时候进行强制类型转化的工作。
它们的测试代码如下:

Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");
i1.do1();
Cls2 i2 = Factory.getInstance("fanxing.factory.dynaFactory.Cls2");
i2.do2()

测试结果为:
cls1...
cls2...
需要注意的是,使用这种方法有几个问题:
  第一, return (U) cls.newInstance();这个语句会有警告性的错误,好在这种错误不是编译性的错误,还是我们可以承受的。
  第二, 编译器不能做类型检查,如Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");这句,换成Cls2 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");也是可以编译过去的。只是在运行的时候才会出错。
  除了上面的方法,还有一种更好的方法。
  这个方法需要我们在运行期内传入的对象是Class对象,当然是经过泛型的Class对象,就像下面的样子:

Class<Cls1> cls = Cls1.class;
try
{
Intf1 obj = cls.newInstance();
obj.do1();
}
catch(Exception e)
{
e.printStackTrace();
}

可以很清楚地看到,这里完全没有了强制类型转化,当然也就没有第一种方法所出现的那两个问题。
运行结果为:
cls1...
根据这个方法,我们可以把前面的初始化方法改造为:

public static <U extends Object> U getInstance(Class<U> cls)
{
try
{
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}

我们来进行以下测试:
Cls1 c1 = Factory.getInstance(Cls1.class);
c1.do1();
测试结果为:
cls1...
这时候,如果我们将上面的测试代码改为:
Cls2 c1 = Factory.getInstance(Cls1.class);
  就会出现编译错误,可以看到,我们的第二种方法的确避免了第一种方法的弱点,但第一种方法的好处是,只需要往初始化方法里输入类名作为参数。
2. 运行期内调用方法
  使用过反射的人都知道,运行期内调用方法后得到的结果也将是一个Object对象。这样的结果在一般的方法反射上也就是可以忍受的了。但是有时候也是不能忍受的,比如,我们想反射List<T>类的iterator()方法。
一般的,我们使用反射可以这么做:
try
{
Class<?> cls = Class.forName("java.util.ArrayList");
Method m = cls.getDeclaredMethod("iterator",new Class[0]);
return m.invoke(this,new Object[0]);
}
catch(Exception e)
{
e.printStackTrace();
return null;
}

当然,这里返回的是一个Object对象,而List<T>类的iterator()的实际返回类型为T。很明显,我们可以将上面的代码做如下的修改:

try
{
Class<?> cls = Class.forName("java.util.ArrayList");
Method m = cls.getDeclaredMethod("iterator",new Class[0]);
return (T)m.invoke(this,new Object[0]);
}
catch(Exception e)
{
e.printStackTrace();
return null;
}

  同样,我们的代码也会遇到警告性的错误。但是我们可以置之不理。
3. 运行期内初始化数组对象
  同样,在运行期内初始化得到的数组也是一个Object对象。就像下面的样子:
Object o = Array.newInstance(int.class,10);
  如果我想在getArray()方法里得到数组对象,就会得到像下面这个样子的代码:
public Object getArray(Class cls,int size)
{
return Array.newInstance(cls,size);
}

  这显然不会令我们满意。因为如果我们输入一个Class<T>类型的参数,就希望返回一个T类型的结果。
在这种想法下,我们就使用泛型将getArray()方法做如下修改:
public T[] getArray(Class<T> cls,int size)
{
return (T[])Array.newInstance(cls,size);
}

  这样的修改,将使我们得到一个比较满意的结果。同样,上面的代码也会得到一个警告性的错误。但是,使用泛型使我们的结果得到了很大的优化。
  上面的几个例子让我们看到了泛型能够帮助反射,而且是大大优化了反射的结果。但在同时,反射也不是被动的接受泛型的帮助,反射同样也可以帮助泛型。这是基于反射的基本工作原理:得到数据的元数据。也就是说,可以通过反射得到泛型的元数据。除此之外,反射也有利于帮助泛型数据运行期内初始化。
下面以一两个简单例子加以说明。
4. 使用反射初始化泛型类
  我们通常会在一个类里这样使用泛型:

public final class Pair<A,B> {
public final A fst;
public final B snd;

public Pair(A fst, B snd) {
this.fst = fst;
this.snd = snd;
}
……
}

  这当然是我们最基本的用法,但常常会这样:编译器希望知道更多的关于这个未知对象如A fst的信息,这样,我们可以在运行期内调用某一些方法。大家说啊,这很容易啊,我们可以把这种未知类型作为参数输入。呵呵,这就对了,有了这样参数,下一步,我们就要使用反射在运行期内调用它的方法。
  关于这样的例子,我在这里不再给出。我在这里给出一个简单一些的例子,就是对泛型类初始化需要调用的构造器。
对于上面的Pair<A,B>类,如果构造器的输入参数的类型不是A和B,而是Class<A>和Class<B>,那么我们就不得不在构造器里使用反射了。

public Pair(Class<A> typeA, Class<B> typeB) {
this.fst = typeA.newInstance();
this.snd = typeB.newInstance();
……
}

  由此可见,对于泛型里的未知类型参数,我们也完全可以和普通类型一样使用反射工具。即可以通过反射对这些未知类型参数做反射所能做到的任何事情。

5. 使用反射得到泛型信息

  关于这一个小结的问题,显得更加得有趣。
  我们还是以上面的Pair<A,B>作为例子,假如我们在一个类中使用到了这个类,如下所示:

public Class PairUser
{
private Pair<String,List> pair;
……
}

  如果我们对PairUser类应用反射,我们可以很轻松的得到该类的属性pair的一些信息,如它是private还是public的,它的类型等等。

  如果我们通过反射得到pair属性的类型为Pair以后,我们知道该类是一个泛型类,那么我们就想进一步知道该泛型类的更进一步的信息。比如,泛型类的类名,泛型参数的信息等等。

  具体到上面的PairUser类的例子,我现在想知道它的属性pair的一些信息,比如,它的类型名、泛型参数的类型,如String和List等等这些信息。所要做的工作如下:

首先是取得这个属性
Field field = PairUser.class.getDeclaredField("pair");
然后是取得属性的泛型类型
Type gType = field.getGenericType();
再判断gType是否为ParameterizedType类型,如果是则转化为ParameterizedType类型的变量
ParameterizedType pType = (ParameterizedType)gType;
取得原始类型
Type rType = pType.getRawType();
然后就可以通过rType.getClass().getName()获得属性pair的类型名。
最后获取参数信息
Type[] tArgs = pType.getActualTypeArguments();
可以通过tArgs[j].getClass().getName()取得属性pair的泛型参数的类型名。
完整的代码如下:

try
{
Field field = PairUser.class.getDeclaredField("pair");
Type gType = field.getGenericType();
if(gType instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType)gType;
Type rType = pType.getRawType();
System.out.println("rawType is instance of " +
rType.getClass().getName());
System.out.println(" (" + rType + ")");
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("actual type arguments are:");
for (int j = 0; j < tArgs.length; j++) {
System.out.println(" instance of " +
tArgs[j].getClass().getName() + ":");
System.out.println(" (" + tArgs[j] + ")");
}
}
else
{
System.out.println("getGenericType is not a ParameterizedType!");
}
}
catch(Exception e)
{
e.printStackTrace();
}

}

输出结果为:
rawType is instance of java.lang.Class
(class Pair)
actual type arguments are:
instance of java.lang.Class:
(class java.lang.String)
instance of java.lang.Class:
(interface java.util.List)
<think>我们正在讨论C#中反射的结合使用。用户要求提供示例用法和实现。根据引用[2]和[4],我们知道C#的在运行时是具体化的,即对于每个不同的类参数,都会生成一个特定的类。同时,反射允许我们在运行时检查类、创建对象、调用方法等。结合反射,我们可以动态地创建并调用其方法。步骤:1.获取的定义(例如:List<>)。2.使用MakeGenericType方法将定义转换为具体的(例如:List<int>)。3.然后,我们可以使用Activator.CreateInstance来创建该类的实例。4.对于方法,我们可以通过反射获取方法定义,然后使用MakeGenericMethod来指定类参数,再调用它。示例:假设我们有一个类:public classMyGenericClass<T>{publicvoid MyMethod(Tvalue){Console.WriteLine(value);}}我们想通过反射来创建MyGenericClass<string>的实例并调用其MyMethod方法。步骤详解:1.获取定义:Type genericTypeDef= typeof(MyGenericClass<>);2.创建具体的:Type constructedType =genericTypeDef.MakeGenericType(typeof(string));3.创建实例:objectinstance =Activator.CreateInstance(constructedType);4.获取MyMethod方法:MethodInfo method= constructedType.GetMethod("MyMethod");5.调用方法:method.Invoke(instance,new object[] {"HelloWorld"});另外,对于方法,例如:public classMyClass{public voidGenericMethod<T>(T value){Console.WriteLine(value);}}使用反射调用方法:1.获取类:Typetype =typeof(MyClass);2.创建实例:object instance= Activator.CreateInstance(type);3.获取方法定义:MethodInfomethodDef= type.GetMethod("GenericMethod");4.创建具体的方法:MethodInfogenericMethod= methodDef.MakeGenericMethod(typeof(string));5.调用:genericMethod.Invoke(instance, newobject[]{ "Hello"});注意:反射操作性能开销较大,应谨慎使用。根据用户要求,我们提供示例代码:```usingSystem;using System.Reflection; publicclass MyGenericClass<T>{public voidMyMethod(T value){Console.WriteLine($"MyMethod calledwith value: {value}");}} publicclass Program{publicstatic voidMain(){//示例1:反射创建类实例并调用方法//获取定义Type genericTypeDef= typeof(MyGenericClass<>);//创建具体类:MyGenericClass<string>Type constructedType =genericTypeDef.MakeGenericType(typeof(string));//创建实例objectinstance= Activator.CreateInstance(constructedType);//获取方法MethodInfo method= constructedType.GetMethod("MyMethod");//调用方法,传入字符串参数method.Invoke(instance,new object[] {"HelloReflection withGenerics!" });//示例2:反射调用方法//假设我们有一个包含方法的类Type myClassType= typeof(MyClass);objectmyClassInstance =Activator.CreateInstance(myClassType);MethodInfo genericMethodDef= myClassType.GetMethod("GenericMethod");//指定方法的类参数为intMethodInfogenericMethod= genericMethodDef.MakeGenericMethod(typeof(int));genericMethod.Invoke(myClassInstance,new object[] {123});} }public classMyClass{publicvoid GenericMethod<T>(Tvalue){Console.WriteLine($"GenericMethodcalled withvalue:{value}");} }```运行结果:MyMethodcalled withvalue:Hello Reflectionwith Generics!GenericMethod calledwith value:123注意:在实际应用中,反射可能会抛出异常(如方法不存在、参数不匹配等),应添加异常处理。相关问题: §§相关问题§§1.反射创建时,如何传递多个类参数?2.如何通过反射获取方法的类参数?3.反射结合使用时有哪些性能问题?如何优化?</think>### C# 反射结合使用示例 在 C# 中将反射结合使用时,核心是通过 `MakeGenericType` 和 `MakeGenericMethod` 实现动态类绑定。以下是具体示例: #### 1. 反射创建类实例 ```csharp // 定义类 public class Repository<T> { public void Add(T entity) => Console.WriteLine($"Added {typeof(T).Name}"); } // 反射创建实例 Type openType = typeof(Repository<>); Type closedType = openType.MakeGenericType(typeof(string)); object instance = Activator.CreateInstance(closedType); // 调用方法 MethodInfo addMethod = closedType.GetMethod("Add"); addMethod.Invoke(instance, new object[] { "Test" }); // 输出: Added String ``` #### 2. 反射调用方法 ```csharp public class DataProcessor { public T Process<T>(T input) => input; } // 动态调用方法 var processor = new DataProcessor(); MethodInfo method = typeof(DataProcessor).GetMethod("Process"); MethodInfo genericMethod = method.MakeGenericMethod(typeof(int)); int result = (int)genericMethod.Invoke(processor, new object[] { 42 }); Console.WriteLine(result); // 输出: 42 ``` #### 3. 检查定义 ```csharp Type listType = typeof(List<>); Console.WriteLine($"Is generic: {listType.IsGenericType}"); // True Console.WriteLine($"Type params: {string.Join(", ", listType.GetGenericArguments())}"); // T ``` #### 关键实现技术: 1. **`MakeGenericType(Type[])`** 将开放(如 `List<>`)转换为具体类(如 `List<string>`) 2. **`MakeGenericMethod(Type[])`** 为方法绑定具体类参数 3. **类判断方法** - `Type.IsGenericType`:检查是否为 - `Type.GetGenericTypeDefinition()`:获取原始定义 - `Type.ContainsGenericParameters`:检查是否存在未绑定的类参数 > **重要注意事项** > - 运行时类检查:当使用 `dynamic` 或反射时,编译器无法进行静态类检查[^4] > - 性能优化:反射操作开销较大,高频场景建议使用缓存或表达式树 > - 类安全:使用 `genericMethod.ReturnType` 和 `ParameterType` 验证类兼容性 --- ### 反射应用场景 1. **动态 ORM 实现** ```csharp Type repoType = typeof(Repository<>).MakeGenericType(entityType); object repository = serviceProvider.GetService(repoType); ``` 2. **插件系统** ```csharp Type pluginInterface = typeof(IPlugin<>).MakeGenericType(pluginType); var plugin = assembly.GetTypes().First(t => pluginInterface.IsAssignableFrom(t)); ``` 3. **序列化工具** ```csharp MethodInfo serializeMethod = typeof(Serializer) .GetMethod("Serialize") .MakeGenericModel(modelType); ``` [^1]: 声明语法参考 [^2]: .NET 运行时对的处理机制 [^4]: 动态类静态类安全权衡
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值