C# 编程基础之 - 反射

253 阅读2分钟
  • 本文已参与「新人创作礼」活动,一起开启掘金创作之路。

什么是反射?

C# 中,查看和操作元数据就称之为反射!反射提高了程序的灵活性和扩展性。说到反射,不得不说到我们 C# 中的System.Type类。

获取任何类型的Type引用的常用方式有如下两种

Type type = typeof(int);

int number = 1;
Type type = number.GetType();

Type是许多反射功能的入口,它实现了很多方法和属性!属性主要是用来获取相关的各种名称的字符串,而方法主要是用来获取对应数据类型的构造函数、方法、事件等东西

static void Main(string[] args)
{
    Type type = typeof(int);

    Console.WriteLine(type.Name);   //  获取当前类型的名称
    Console.WriteLine(type.FullName);// 获取当前类型的名称包括了命名空间
    Console.WriteLine(type.IsAbstract); //判断是否是抽象的
    Console.WriteLine(type.IsValueType); // 判断是否是值类型
    Console.WriteLine(type.IsArray); // 该值指示类型是否为数组
    .....
}

image.png

属性赋值

使用反射为私有字段,并且属性赋值

public class Program
{
    static void Main(string[] args)
    {
        //User user = new User();
        //user.UserId // 在这里无法赋值

        Type type = typeof(User);
        //使用GetConstructors 可以获取所有的构造函数
        ConstructorInfo[] constructors = type.GetConstructors();

        // 使用Invoke调用私有构造函数
        var obj = constructors[0].Invoke(null);

        // 动态创建对象
        var instance = Activator.CreateInstance(typeof(User)) as User;

        // 获取所有的实例成员
        var fileds = type.GetProperties();

        // 给私有属性赋值
        foreach (var item in fileds) 
        {
            if (item.Name.Equals("UserId"))
            {
                item.SetValue(instance, 123);
            }
        }

        Console.WriteLine($"属性赋值之后的私有属性变化为:{instance.UserId}");
    }
}

public class User 
{
    public User() 
    {
        Console.WriteLine("构造函数调用中**********");
    }

    public int UserId { get; private set; }

    public string UserName { get; set; }

    public string Age { get; set; }

    public int Sex { get; set; }
}

注意:GetConstructors()方法可以获取所有的构造函数,但是如果需要调用私有构造函数这需要使用重载方法加入BindingFlags 枚举值

反射与特性

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。在 C# 中,通过用方括号 ([]) 将特性名称括起来,并置于应用该特性的实体的声明上方以指定特性。如下代码所示:

public class User 
{
    [Required(ErrorMessage = "用户Id是必填的")]
    public int UserId { get; set; }
}

如何创建自定义特性

可通过定义特性类创建自己的自定义特性,特性类是直接或间接派生自 Attribute的类,可快速轻松地识别元数据中的特性定义。

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class)]
public class UserTestAttribute : Attribute
{

}

[UserTest]
public class User 
{
    public int UserId { get; set; }
}

注意:AttributeUsage用于指定你可以放置的元素上,如下是枚举值

image.png

使用反射获取特性

还是上面代码,我们为自定义属性添加一个构造函数,将传入的Message赋值,然后我们通过反射来获取这个值!

[AttributeUsage(AttributeTargets.Class)]
public class UserTestAttribute : Attribute
{
    public string Message { get; set; }
    public UserTestAttribute(string message)
    {
        Message = message;
    }
}
  • 定义好自定义特性之后我们在元素上使用这个特性需要传递对应的参数,上面的写法是必填的,如果需要选填我们可以自定义一个无参的构造函数即可
[UserTest("This is Test Message")]
public class User
{

}
  • 接下来我们通过反射来获取特性,这里我们使用GetCustomAttributes(返回一个数组,它包含应用于此成员的所有自定义属性)来获取!
static void Main(string[] args)
{
    Type type = typeof(User);
    object[] customAttr = type.GetCustomAttributes(false);

    foreach (var item in customAttr)
    {
        if (item is UserTestAttribute)
        {
            Console.WriteLine($"获取UserTestAttribute Message:{((UserTestAttribute)item).Message}");
        }

    }
}

image.png

早期绑定:在CLR运行代码之前就将他们加载然后建立类型对象,而晚期绑定就是在运行时建立对象。

总结

通过反射的一些有点和特性,我们可以使用它做非常多的事情,本文中讲的也比较浅,还有一些没有讲到像延迟绑定的方法和属性,像我们经常使用的ORM框架,.NET Core中常用的IOC容器等等,他应用于我们平时使用的非常多的地方,感兴趣的朋友可以深入去了解,后续我会使用反射为大家实现一个简单的ORM框架。

本文如果有讲述的不对的地方,还请各位大佬多多建议,我会听取大家的意见,及时更新文章,感谢大家!