反射和注解

C#语言提供了反射(Reflection)的特性,通过反射我们能够获取程序集内部的类、属性、注解等,还可以根据类名动态实例化对象、根据方法名调用方法。而注解(Attribute)配合反射使用能实现很多强大的功能,比如依赖注入、数据验证等。

反射

Type 类对象

C#中,Type是一个表示类型的类对象,类似Java中的Class。其中包含了类型、全限定名、父类类型、成员属性等重要信息。获取类对象有三种方式:

  1. 通过typeof运算符获取类型
  2. 调用object.GetType()获取类型
  3. 调用Type.GetType(string typeName)根据类名获取类型

以下例子代码中,使用这三种方式获取类型对象:

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("Gacfox.Demo.DemoNetCore.Student");
    }
}

获取类成员

Type对象中包含了一系列方法,能够获取类的字段、属性、方法等。

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

Activator能够根据Type类型实例化对象。

Student s = Activator.CreateInstance(typeof(Student)) as Student;

注解

注:实际上C#的Attribute直译是“特性”,这个翻译就比较奇葩了,我这里就沿用Java的叫法“注解”。不过实际上Java的注解是仿照C#的语法特性,是后出现的。

注解的使用

Attribute一般都需要配合反射来使用,这里我们直接看一个例子,下面代码中我们实现了一个基于Attribute的数据验证功能。

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。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。