C#实现反射
一、引言
任务
2.2 构建一个components.txt文件,每行代表一个类(比如形状类Shape)的名字(这些类可以来自系统库,或者自己构造);程序逐行扫描该文件,构建对应的类的对象。要求:
1)并把这些对象放到数组中;
- 列出每个类的字段、方法;
3)让用户选择,使得用户可以调用所需要的方法(操作)
4)系统随机选择对象(比如形状),随机的执行其操作。从而看系统演化。可能的话,进行界面展示
二、实验环境
Visual stdio 2017
Windows窗体应用
三、实验过程
思路
- 创建一个类,用于后续反射其信息
- 命令行编译命令 使 .cs生成.dll文件
- 构建一个components.txt文件
- 利用反射查看成员信息
1.创建一个类
内容包括各个shape的面积和周长计算公式等内容(后续有完整代码)
2.命令行编译命令 使 .cs生成.dll文件
命令行编译命令
步骤:
然后逐行输入以下命令语句
path C:\Windows\Microsoft.NET\Framework\v2.0.50727
cd C:\Users\lenovo\source\repos\shiyan3\shiyan3
csc/target:library Graphic.cs
path C:\Windows\Microsoft.NET\Framework\v2.0.50727里有对C#进行编译的程序csc.exe)
(cd C:\Users\lenovo\source\repos\shiyan3\shiyan3 是指项目的位置)但由于此路径在我的电脑上,所以别的电脑打开不一定能运行此代码Orz)
(csc/target:library Graphic.cs ->”csc/target:library”是常用的模块名 ,该命令让编译器创建一个动态链接库(DLL),而不是一个可执行文件(EXE))
构建一个components.txt文件
构components.txt文件
实验要求是构建一个components.txt文件,每行代表一个类.程序逐行扫描该文件,构建对应的类的对象,所以在其中写入:
Rectangle
Triangle
Sphere
Ellipse
读取文件:
//程序逐行扫描components.txt文件,构建对应的类的对象
string path = "components.txt";
FileStream FFLIE = File.OpenRead(path);
StreamReader STREAMR = new StreamReader(FFLIE, Encoding.Default);
FFLIE.Seek(0, SeekOrigin.Begin);
4.利用反射查看成员信息
4.1
接下来,在Program.cs中, 利用反射,来读取字段的字段、方法等
首先加入内存空间
using System.Reflection;
using System.Collections;
using System.IO;
4.2
反射具体知识点:
反射是一种机制,通过这种机制可以知道一个未知类型的类型信息。
既然叫反射,就像拿一面透视镜,它可以反射出我们本不能看见信息.
反射中常用的类
1.Type类
System.Reflection是反射的命名空间,而Type类为System.Reflection功能的根,也是访问元数据的主要方式。
Type类表示类型声明,包括类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。
得到一个Type实例的三种方法如下:
(1)使用System.Object.GetType(),例如:
Person pe=new Person(); //定义pe为person类的一个对象
Type t=pe.GetType(); //我的代码用的是这个方法Type fanshelei = gelei.GetType();;
这样t为pe的Type对象。
(2)使用System.Type.GetType()静态方法,参数为类型的完全限定名。例如:
Type t=Type.GetType(“MyNs.Person”);
其中,MyNs.Person为MyNs命名空间中的Person类,这样t为该类的Type对象。
(3)使用typeof运算符,例如:
Type t=typeof(Person);
其中Person为一个类,这样t为该类的Type对象。
2.System.Reflection反射命名空间
System.Reflection反射命名空间包含提供加载类型、方法和字段的有组织的视图的类和接口,具有动态创建和调用类型的功能。其中主要的类及其功能如下:
Assembly类:通过它可以加载、了解和操作一个程序集。
//此次作业代码用到 :
Assembly assembly = Assembly.LoadFrom(“Graphic.dll”);//加载程序集(cs文件生成.dll文件,.dll文件是一个程序集)
Console.WriteLine(“name of assembley:” + assembly.GetName());//输出程序集的Name
AssemblyName类:通过它可以找到大量隐藏在程序集的身份中的信息,如版本信息、区域信息等。
ConstructorInfo类:用于发现构造函数及调用构造函数。通过对ConstructorInfo调用Invoke来创建对象,其中ConstructorInfo是由Type对象的GetConstructors或GetConstructor方法返回的。
EventInfo类:通过它可以找到事件的信息。
FieldInfo类:通过它可以找到字段的信息。//此次作业代码用到 FieldInfo[] ziduannum = fanshelei.GetFields();
MethodInfo类:通过它可以找到方法的信息。//此次作业代码用到 MethodInfo[] methodnum = fanshelei.GetMethods();
ParameterInfo类:通过它可以找到参数的信息。
PropertyInfo类:通过它可以找到属性的信息。
MemberInfo类:它是一个抽象基类,为EventInfo、FieldInfo、MethodInfo、PropertyInfo等类型定义了公共的行为。
Module类:用来访问带有多文件程序集的给定模块。
DefaultMemberAttribute类:定义某类型的成员,该成员是InvokeMember使用的默认成员。
步骤:通过反射输出用户所需要的查看的类的方法、字段信息。
先通过Type的GetType() 方法获取用户所需要的查看的类的Type对象mytype,然后用Type类的GetMethods()、GetFields()分别获取mytype对象的方法、字段并输出。程序如下:
由上述知Assembly类:通过它可以加载、了解和操作一个程序集。
Assembly assembly = Assembly.LoadFrom(“Graphic.dll”);//加载程序集(cs文件生成.dll文件,.dll文件是一个程序集)
Console.WriteLine(“name of assembley:” + assembly.GetName());//输出程序集的Name
GetType:返回一个指定类型的Type对象
MethodInfo:找到方法的信息
FieldInfo:找到字段的信息
if (gelei != null)
Console.WriteLine("<{0}>类名:{1}", classList.Count, fanshelei.Name);
//列出每个类的字段、方法;
MethodInfo[] methodnum = fanshelei.GetMethods();
Console.WriteLine(" {0}的方法个数:{1}", fanshelei.FullName, methodnum.Length);
for (int j = 0; j < methodnum.Length; j++)
{
Console.WriteLine(" ({0}){1}", j + 1, methodnum[j].Name);
}
FieldInfo[] ziduannum = fanshelei.GetFields();
Console.WriteLine(" {0}的字段个数:{1}", fanshelei.FullName, ziduannum.Length);
for (int k = 0; k < ziduannum.Length; k++)
{
Console.WriteLine(" ({0}){1}", k + 1, ziduannum[k].Name);
}
4.3让用户选择,使得用户可以调用所需要的方法(操作)
//让用户选择,使得用户可以调用所需要的方法(操作)
Console.Write(“请选择类对象<1-4>或退出0:”);
int leiduixiang = int.Parse(Console.ReadLine());
//让用户选择,使得用户可以调用所需要的方法(操作)
Console.Write("请选择类对象<1-4>或退出0:");
int leiduixiang = int.Parse(Console.ReadLine());
if (leiduixiang == 0)
break;
if (leiduixiang < 0 || leiduixiang > 4)
{
Console.WriteLine("输入错误!\n***********************");
continue;
}
Type curtype = classList[leiduixiang - 1].GetType();
MethodInfo[] methodnumss = curtype.GetMethods();
Console.WriteLine(" {0}的方法个数:{1}", curtype.FullName, methodnumss.Length);
for (int i = 0; i < methodnumss.Length; i++)
Console.WriteLine(" ({0}){1}", i + 1, methodnumss[i].Name);
Console.Write("请选择方法(except 4):");
int nowfangfa = int.Parse(Console.ReadLine());
object myshape = classList[leiduixiang - 1];
if (nowfangfa < 1 || nowfangfa > 6 || nowfangfa == 4)
Console.WriteLine("输入错误!\n***********************");
else
Console.WriteLine(myshape.GetType().Name + "——" + methodnumss[nowfangfa - 1].Name + ":" + methodnumss[nowfangfa - 1].Invoke(myshape, null) + "\n***********************" +
"");
4.4
系统随机选择对象(比如形状),随机的执行其操作。从而看系统演化。可能的话,进行界面展示
case “1”😕/系统随机生成
DateTime结构类位于System命名空间中,DateTime值类型表示值范围在公元0001年1月1日午夜12:00:00到公元9999年12月31日晚上11:59:59之间的日期和时间。
Millisecond 获取此实例所表示日期的毫秒部分
TimeSpan ttime = new DateTime() - new DateTime(1999,12, 3);
float hm = ttime.Milliseconds;
Random myrandom = new Random();
int classNum = myrandom.Next(0, 3);
object myobject = classList[classNum];//随机选择一个类
int numfangfa;
while (true)
{
numfangfa = myrandom.Next(1, 6);
if (numfangfa != 4)
break;
}
Type leixing = classList[classNum].GetType();
MethodInfo[] methoddn = leixing.GetMethods();
Console.WriteLine(leixing.Name + "——" + methoddn[numfangfa - 1].Name + ":" + methoddn[numfangfa - 1].Invoke(myobject, null) + "\n***********************");
break;
代码会生成语句:Triangle——Perimeter:11.5440037453175
完整代码:
图形类
using System;
namespace csharp_exp4
{
public class Rectangle//长方形
{
public const double PI = Math.PI;
protected double x, y;
public Rectangle() { }
public Rectangle(double x1, double y1)
{
x = x1;
y = y1;
}
public virtual double Area()//面积
{
return x * y;
}
public virtual double Perimeter()//周长
{
return (x + y) * 2;
}
}
public class Triangle : Rectangle//等腰三角形
{
public Triangle(double x, double y) : base(x, y)//宽,高
{ }
public override double Area()
{
return x * y / 2;
}
public override double Perimeter()
{
return Math.Sqrt(x * x / 4 + y * y) * 2 + x;
}
}
public class Sphere : Rectangle//圆
{
public Sphere(double r, double y) : base(r, 0) { }
public override double Area()
{
return PI * x * x;
}
public override double Perimeter()
{
return 2 * PI * x;
}
}
public class ellipse : Rectangle//椭圆
{
public ellipse(double a, double b) : base(a, b) { }
public override double Area()
{
return PI * x * y;
}
public override double Perimeter()
{
return 2 * PI * y + 4 * (x - y);
}
}
}
Program.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace shiyan3
{
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.LoadFrom("Graphic.dll");//加载程序集:cs文件生成.dll文件,.dll文件是一个程序集
Console.WriteLine("name of assembley:" + assembly.GetName());//程序集的 名称
Type[] types = assembly.GetTypes();
//程序逐行扫描components.txt文件,构建对应的类的对象
string path = "components.txt";
FileStream FFLIE = File.OpenRead(path);
StreamReader STREAMR = new StreamReader(FFLIE, Encoding.Default);
FFLIE.Seek(0, SeekOrigin.Begin);
string classname;
string nsp = "csharp_exp4.";
//对象放到数组中
ArrayList classList = new ArrayList();
while (STREAMR.Peek() > -1)
{
classname = STREAMR.ReadLine();
object[] ars = { 3, 4 };
object gelei = assembly.CreateInstance(nsp + classname, true, BindingFlags.Default, null, ars, null, null);
classList.Add(gelei);
Type fanshelei = gelei.GetType();
if (gelei != null)
Console.WriteLine("<{0}>类名:{1}", classList.Count, fanshelei.Name);
//列出每个类的字段、方法;
MethodInfo[] methodnum = fanshelei.GetMethods();
Console.WriteLine(" {0}的方法个数:{1}", fanshelei.FullName, methodnum.Length);
for (int j = 0; j < methodnum.Length; j++)
{
Console.WriteLine(" ({0}){1}", j + 1, methodnum[j].Name);
}
FieldInfo[] ziduannum = fanshelei.GetFields();
Console.WriteLine(" {0}的字段个数:{1}", fanshelei.FullName, ziduannum.Length);
for (int k = 0; k < ziduannum.Length; k++)
{
Console.WriteLine(" ({0}){1}", k + 1, ziduannum[k].Name);
}
}
Console.Write("\n************1系统操作,2选择操作:*********");
string kase = Console.ReadLine();
switch (kase)
{
case "1":
TimeSpan ttime = new DateTime() - new DateTime(1970, 1, 1);
float hm = ttime.Milliseconds;
Random myrandom = new Random();
int classNum = myrandom.Next(0, 3);
object myobject = classList[classNum];//随机选择一个类
int numfangfa;
while (true)
{
numfangfa = myrandom.Next(1, 6);
if (numfangfa != 4)
break;
}
Type leixing = classList[classNum].GetType();
MethodInfo[] methoddn = leixing.GetMethods();
Console.WriteLine(leixing.Name + "——" + methoddn[numfangfa - 1].Name + ":" + methoddn[numfangfa - 1].Invoke(myobject, null) + "\n***********************");
break;
case "2":
while (true)
{
//让用户选择,使得用户可以调用所需要的方法(操作)
Console.Write("请选择类对象<1-4>或退出0:");
int leiduixiang = int.Parse(Console.ReadLine());
if (leiduixiang == 0)
break;
if (leiduixiang < 0 || leiduixiang > 4)
{
Console.WriteLine("输入错误!\n***********************");
continue;
}
Type curtype = classList[leiduixiang - 1].GetType();
MethodInfo[] methodnumss = curtype.GetMethods();
Console.WriteLine(" {0}的方法个数:{1}", curtype.FullName, methodnumss.Length);
for (int i = 0; i < methodnumss.Length; i++)
Console.WriteLine(" ({0}){1}", i + 1, methodnumss[i].Name);
Console.Write("请选择方法(except 4):");
int nowfangfa = int.Parse(Console.ReadLine());
object myshape = classList[leiduixiang - 1];
if (nowfangfa < 1 || nowfangfa > 6 || nowfangfa == 4)
Console.WriteLine("输入错误!\n***********************");
else
Console.WriteLine(myshape.GetType().Name + "——" + methodnumss[nowfangfa - 1].Name + ":" + methodnumss[nowfangfa - 1].Invoke(myshape, null) + "\n***********************" +
"");
}
break;
}
Console.ReadKey();
}
}
}
总结
即使是未知的类,也可以有一把照妖镜.
参考资料:
https://www.pianshen.com/article/2920254015/