反射 特性
反射:从exe文件把自己定义的类型和成员查找出来 引用system.reflection
想调用一个实例化的字段需要new一个当前对象去调用
反射的好处:可以通过配置文件和路径得到定义的类中的所有成员也可通过对象类调用所有成员私有的用BindingFlags.NonPublic|BindingFlags.Instance调用
.ctor:构造函数
练习分别调用方法 属性 字段构造函数(有参 无参) 索引器
反射属于命名空间的类型和成员才能反射出来命名空间内引用的成员不能反射出来。
反射的使用:
Assembly ass = Assembly.LoadFrom(@"C:\Users\rui\Documents\Visual Studio 2010\Projects\FanShe\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe");//使用Assembly的LoadForm方法加载程序集
object o = ass.CreateInstance("ConsoleApplication1.Program");//从程序集中查找指定的类型 参数命名空间
//调用方法 要反射的对象.GetType().GetMethod("方法名");
Type type = o.GetType();
MethodInfo mi = type.GetMethod("Method");
mi.Invoke(o, null);
//调用字段 要反射的对象.GetType().GetField("字段名");
FieldInfo fi = o.GetType().GetField("str", BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine(fi.GetValue(o));
//调用属性 要反射的对象.GetType().GetProperty("属性名");
PropertyInfo pi = o.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
pi.SetValue(o, "属性", null);//可以传一个参数过去
Console.WriteLine(pi.GetValue(o, null));// 要反射的对象.GetType().GetProperty("属性名").GetValue(要反射的对象,null); 获得字段的属性
//调用一个无参的构造函数 要反射的对象.GetType().GetConstructor(new Type[0]);
ConstructorInfo ci = o.GetType().GetConstructor(new Type[0]);
object Obj = ci.Invoke(null);
//调用有参的构造函数
Type[] type2 = new Type[] { typeof(string) };//传入参数类型
object[] oo = new object[] { "hello" };//接受对象类型
ConstructorInfo ci1 = o.GetType().GetConstructor(type2);
ci1.Invoke(oo);
特性Attribute:类中的附属信息 特性与程序实体关联后即可在运行时使用名为反射的技术查询属性
特性已两种形式出现:
一种是在公共语言运行库 (CLR) 中定义的属性。
另一种是可以创建的用于向代码中添加附加信息的自定义属性。此信息可在以后以编程方式检索。
特性的特点:
属性可向程序中添加元数据。元数据是嵌入程序中的信息,如编译器指令或数据描述。
程序可以使用反射检查自己的元数据。
通常使用属性与 COM 交互。
自定义特性:
特性类实例化时需要放在括号“[ ]”中,
语法: [attributeClass(定位参数1,… 命名参数1,…)]
自定义特性有两种类型:
定位参数:相当于实例类的构造函数
定义一个构造函数:
命名参数:相当于实例类的属性,可以赋值也可以不赋
利用特性实现数据库的增删改查(这个可以对数据库中的数据进行操作)
首先定义特性类来与表想关联
定义特性的方法如下:
首先声明一个类来保存数据
[AttributeUsage(AttributeTargets.Class)]//对这个特性进行使用的限制 这里AttributeTargets后面是Class即这个特性只能加在类型上 特性可以加在对任何程序元素上 要靠在这里的限定 如可以加在字段 ,委托,构造函数,类,属性,枚举,事件,接口,方法,模块等
//还可以设置命名参数,AllowMultiple等于true表示可以为程序集设计多个特性 false是除了目前设定的不能再为程序集设计别的特性了
//还可以加Inherited表示该特性是否允许派生类继承或者允许重载
//定义类型的特性
class TableAttrribute : Attribute//这个特性类一定要继承Attritube
{
//声明一个定位参数,即构造函数
string tablename;
public TableAttrribute(string tablename)
{
this.tablename = tablename;
}
//声明一个对外公布的属性
public string TableName
{
get
{
return tablename;
}
}
}
//定义属性的特性
//定义特性属性来与表中的字段相关联
[AttributeUsage(AttributeTargets.Property)]
class FieldAttrribute : Attribute
{
string fieldname;
public FieldAttrribute(string fieldname)
{
this.fieldname = fieldname;
}
public string FieldName
{
get
{
return fieldname;
}
}
public bool IsPrimary//因为这里用到ID与表映射时要考虑到是否是主键
{
get;
set;
}
}
//定义一个Blog类来保存业务逻辑数据,并定义其中的字段
[TableAttrribute("Blog")]//使用特性,这里的"Blog"相当于表中的字段,与业务逻辑里的类型相映射
class Blog : IEntify
{
[FieldAttrribute("ID", IsPrimary = true)]
public int ID//我们在业务逻辑中定义的字段,想要和表关联起来要对字段也设置特性
{
get;
set;
}
[FieldAttrribute("biaoti")]
public string Title
{
get;
set;
}
[FieldAttrribute("zuozhe")]
public string Author
{
get;
set;
}
[FieldAttrribute("shijian")]
public string CreateTime
{
get;
set;
}
}
//定义一个实体接口 方便多个实体时将增删改查分别封装到方法中
interface IEntify
{
}
使用特性分别反射出表名和字段及字段中的数据:
以删除为例:
static string Delete(IEntify entiy)
{
//delete语句:delete 表名 where 主键名=值
//首先要用反射来把表中的数据反射出来
TableAttrribute ta = entiy.GetType().GetCustomAttributes(true)[0] as TableAttrribute;//获取blog这个对象的所有类型的中的自定义特性,然后把它转换成我们接下来要使用的TableAttribute类型
string tablename = ta.TableName;//把特性中的TableName属性赋给一个局部变量tablename 来对应sql语句中的中的表名。
//然后找主键名和值
string filename = "";
string filevalue = "";
foreach (PropertyInfo pi in entiy.GetType().GetProperties())//遍历blog所有的属性来反射数据库的特性
{
FieldAttrribute fa = pi.GetCustomAttributes(true)[0] as FieldAttrribute;//获取属性中的所有特性并把它转换为要使用的FieldAttrribute特性
if (fa.IsPrimary)//判断是否为主键
{
filename = fa.FieldName; //把特性中的FieldName属性赋给局部变量filename
filevalue = pi.GetValue(entiy, null).ToString();//把特性中的属性值赋给局部变量filevalue
}
}
string sql = "delete " + tablename + " where " + filename + " = " + filevalue;//组装sql语句
return sql;
}
更新:
static string Update(IEntify entiy)
{
//update语句:update 表名 set 字段1=值1,字段2=值2,字段3=值3 where 主键名=值
//反射出表名
TableAttrribute ta = entiy.GetType().GetCustomAttributes(true)[0] as TableAttrribute;
string tablename = ta.TableName;
//反射表中的字段数据
//巧妙之处;用一个 HashTable来存储,哈希表的键对应字段 哈希表的值 对应值
Dictionary<string, string> pro = new Dictionary<string, string>();
string filename = "";
string filevalue = "";
foreach (PropertyInfo pi in entiy.GetType().GetProperties())
{
FieldAttrribute fa = pi.GetType().GetCustomAttributes(true)[0] as FieldAttrribute;
if (fa.IsPrimary)//找出主键
{
filename = fa.FieldName;
filevalue = pi.GetValue(entiy, null).ToString();
}
else//把不是主键的字段放到哈希表里面
{
pro.Add(fa.FieldName, pi.GetValue(entiy, null).ToString());
}
}
//构造Update语句
string sqlup = "update " + tablename + " set ";
//set的不是一个字段和值 想到了把字段1=值1,字段2=值2,字段3=值3字符串叠加后再传到总的SQL语句中
foreach (string key in pro.Keys)
{
sqlup += key + "=" + pro[key] + ",";
}
sqlup += sqlup.TrimEnd(',') + " where " + filename + "=" + filevalue;
return sqlup;
}