C#语言提供了反射(Reflection)的特性,通过反射我们能够获取程序集内部的类、属性、注解等,还可以根据类名动态实例化对象、根据方法名调用方法。而注解(Attribute)配合反射使用能实现很多强大的功能,比如依赖注入、数据验证等。
C#中,Type
是一个表示类型的类对象,类似Java中的Class
。其中包含了类型、全限定名、父类类型、成员属性等重要信息。获取类对象有三种方式:
typeof
运算符获取类型object.GetType()
获取类型Type.GetType(string typeName)
根据类名获取类型以下例子代码中,使用这三种方式获取类型对象:
using System;
namespace Gacfox.Demo.DemoNetCore
{
class Student { }
class Program
{
static void Main()
{
// 通过typeof获取类型
Type t1 = typeof(Student);
// 通过对象获取类型
Student s = new Student();
Type t2 = s.GetType();
// 通过字符串获取类型
Type t3 = Type.GetType("Demo.Student");
}
}
}
Type
对象中包含了一系列方法,能够获取类的字段、属性、方法等。
using System;
using System.Reflection;
namespace Gacfox.Demo.DemoNetCore
{
class Student
{
public int Id;
public string StuName { get; set; }
}
class Program
{
static void Main()
{
Type type = typeof(Student);
// 获取public字段
FieldInfo[] fieldInfos = type.GetFields();
// 获取属性
PropertyInfo[] propertyInfos = type.GetProperties();
// 获取方法
MethodInfo[] methodInfos = type.GetMethods();
}
}
}
Activator
能够根据Type
类型实例化对象。
Student s = Activator.CreateInstance(typeof(Student)) as Student;
注:实际上C#的Attribute直译是“特性”,这个翻译就比较奇葩了,我这里就沿用Java的叫法“注解”。不过实际上Java的注解是仿照C#的语法特性,是后出现的。
Attribute
一般都需要配合反射来使用,这里我们直接看一个例子,下面代码中我们实现了一个基于Attribute
的数据验证功能。
using System;
using System.Reflection;
namespace Gacfox.Demo.DemoNetCore
{
[AttributeUsage(AttributeTargets.Property)]
public class MaxLength : Attribute
{
public int Length { get; set; }
public MaxLength(int length)
{
this.Length = length;
}
}
class ValidateUtil
{
public static bool Valid(object o)
{
Type t = o.GetType();
foreach (PropertyInfo info in t.GetProperties())
{
foreach (Attribute attribute in info.GetCustomAttributes())
{
if (attribute.GetType() == typeof(MaxLength))
{
MaxLength ml = attribute as MaxLength;
string oPropertyValue = info.GetValue(o) as string;
if (ml != null && oPropertyValue != null)
{
if (oPropertyValue.Length > ml.Length)
{
return false;
}
}
}
}
}
return true;
}
}
class Student
{
[MaxLength(3)] public string StudentName { get; set; }
}
class Program
{
static void Main()
{
Student student = new Student();
student.StudentName = "Tommy";
bool result = ValidateUtil.Valid(student);
Console.WriteLine(result);
}
}
}
代码非常简单,我们自定义了一个MaxLength
注解,[AttributeUsage(AttributeTargets.Property)]
表示该注解可以标注在类的属性上。我们的自定义注解继承Attribute
类,其内部维护了一个整数。
数据验证逻辑在ValidateUtil
类中,代码通过反射获取一个对象的所有属性,并获取属性的注解。如果我们自定义注解存在,就比较属性值字符串长度和校验长度,如果不符合校验规则就返回false
。