二、C#反射学习笔记

反射知识点对应的学习代码

1、为什么学习反射?

反射其实无处不在,ORM/MVC/IOC等框架都用到了反射。
高级语言到机器码的编译原理图
从高级语言到机器码的整个编译运行过程中,反射实际上就是在编译之后的DLL/EXE文件上起作用,DLL/EXE文件中又包含metadata文件和IL文件。
IL文件是对标于C#代码的代码,不太好阅读。
IL编码
IL编码可通过ILSpy应用进行读取,ILSpy是一个逆向应用,可以把我们所写的各种高级语言的单词编译后生成的DLL/EXE文件反编译回来。
metadata是一个清单数据,就是元数据。
List从元数据

2、什么是反射?

反射是一个工具,是微软提供的一个帮助类库,可以读取并使用metadata文件,命名空间是System.Reflection。

3、反射创建对象调用方法一般步骤

  1. 引入System.Reflection命名空间
  2. 动态加载
  3. 获取类型
  4. 创建对象
  5. 类型转换
  6. 调用方法
    具体方法如下代码所示:
/// <summary>
/// 通过反射,创建对象、调用方法
/// </summary>
public static void ShowReflection()
{
    Console.WriteLine("------------------------------------ShowReflection-------------------------------------");

    //1.引入System.Reflection

    //2.动态加载
    //方式一:对应项目的DLL文件名,不加后缀名,一般不推荐使用
    Assembly assembly1 = Assembly.Load("Reflection.SqlServer");
    //方式二:对应项目的DLL文件名,需要后缀名,推荐使用该方式
    Assembly assembly2 = Assembly.LoadFrom("Reflection.SqlServer.dll");
    //方式三:对应项目DLL文件的全路径,需要后缀名
    Assembly assembly3 = Assembly.LoadFrom(@"G:\study\.NET\高级班14期\01ExerciseProject\Reflection\Reflection04\Reflection04\bin\Debug\netcoreapp3.1\Reflection.SqlServer.dll");
    //方式四:对应项目DLL文件的全路径,需要后缀名
    Assembly assembly4 = Assembly.LoadFile(@"G:\study\.NET\高级班14期\01ExerciseProject\Reflection\Reflection04\Reflection04\bin\Debug\netcoreapp3.1\Reflection.SqlServer.dll");

    //3.获取类型
    //需要类型的全类名,全类名=命名空间+类名
    Type type = assembly2.GetType("Reflection.SqlServer.SqlServerHelper");

    //4.创建对象
    //方式一:如果为空时,会报异常
    object obj = Activator.CreateInstance(type);
    //因为类型是object声明的,不能调用SqlServerHelper类型声明的Query(string sPar)方法
    //在C#中,变量的声明是编译时决定的

    //方式二:动态类型,dynamic是运行时决定的,可以避开编译器的检查
    dynamic dmc = Activator.CreateInstance(type);
    dmc.Query("dynamic ShowReflection");
    //dmc.Show();
    //dmc调用Show()方法在编译时不会报错,这是因为dynamic能够避开编译器的检查
    //但是由于SqlServerHelper类中没有定义Show()方法,因此dmc在调用Show()方法时会报错
    //Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
    //“'DB.SqlServer.MySqlHelper' does not contain a definition for 'Show'”

    //5.类型转换
    //方式一:强制转换,如果实际类型不一致会报错
    IDBHelper dbHelper1 = (IDBHelper)obj;
    //方式二:
    IDBHelper dbHelper2 = obj as IDBHelper;

    //6.调用方法
    dbHelper1.Query("强制转换方式 ShowReflection");
    dbHelper2.Query("as方式 ShowReflection");
}

4、封装反射操作,实现细节解耦

public class SimpleFactory
{
    public static T CreateInstance<T>() where T : class
    {
        string parStr = ReadConfig.GetConfig("ReflectionConfig");
        string[] pars = parStr.Split(',');
        string dllName = pars[1];
        string typeName = pars[0];
        Assembly assembly = Assembly.LoadFrom(dllName);
        Type type = assembly.GetType(typeName);
        object obj = Activator.CreateInstance(type);

        return obj as T;
    }
}

5、性能对比

/// <summary>
/// 反射的局限性
/// 性能比较差
/// 
/// 反射中最耗时的操作是动态加载,读取dll文件,Assembly.LoadFrom(dll文件名,需要加载后缀名)
/// 其次的耗时操作是获取类型,assembly.GetType(全类名=命名空间+类名)
/// 
/// 反射确实会存在性能问题
/// 但要扬长避短,反射的优点还是显而易见的,许多地方目前根本离不开反射
/// 经过优化之后,其实性能没有想象的那么差
/// </summary>
public static void PerComp()
{
    long commonTime = 0;
    long reflectTime = 0;
    long optimitTime = 0;
    Stopwatch commonWatch = new Stopwatch();
    Stopwatch reflectWatch = new Stopwatch();
    Stopwatch optimitWatch = new Stopwatch();

    commonWatch.Start();
    for(int i = 0; i < 1000_000; i++)
    {
        OracleHelper helper = new OracleHelper();
        helper.Query("");
    }
    commonWatch.Stop();
    commonTime = commonWatch.ElapsedMilliseconds;

    reflectWatch.Start();
    for(int i = 0; i < 1000_000; i++)
    {
        Assembly assembly1 = Assembly.LoadFrom("Reflection.Oracle.dll");
        Type type1 = assembly1.GetType("Reflection.Oracle.OracleHelper");
        IDBHelper helper1 = Activator.CreateInstance(type1) as IDBHelper;
        helper1.Query("");
    }
    reflectWatch.Stop();
    reflectTime = reflectWatch.ElapsedMilliseconds;

    optimitWatch.Start();
    Assembly assembly2 = Assembly.LoadFrom("Reflection.Oracle.dll");
    Type type2 = assembly2.GetType("Reflection.Oracle.OracleHelper");
    for(int i = 0; i < 1000_000; i++)
    {
        IDBHelper helper2 = Activator.CreateInstance(type2) as IDBHelper;
        helper2.Query("");
    }
    optimitWatch.Stop();
    optimitTime = optimitWatch.ElapsedMilliseconds;

    Console.WriteLine($"普通方法耗时:{commonTime},反射方法耗时:{reflectTime},优化方法耗时:{optimitTime}");
}

性能对比

6、通过反射创建对象、调用方法

实例:

public class FuncAndMethod
{
    #region 构造函数
    /// <summary>
    /// 无参构造函数
    /// </summary>
    public FuncAndMethod()
    {
        Console.WriteLine($"{this.GetType().Name}的无参构造函数");
    }

    public FuncAndMethod(int iPar)
    {
        Console.WriteLine($"{this.GetType().Name}的单参构造函数,参数类型:{iPar.GetType().Name},参数:{iPar}");
    }

    public FuncAndMethod(string sPar)
    {
        Console.WriteLine($"{this.GetType().Name}的单参构造函数,参数类型:{sPar.GetType().Name},参数:{sPar}");
    }

    public FuncAndMethod(int iPar,string sPar)
    {
        Console.WriteLine($"{this.GetType().Name}的双参构造函数,参数类型:{iPar.GetType().Name}{sPar.GetType().Name}," +
            $"参数:{iPar}{sPar}");
    }

    private FuncAndMethod(string sPar, int iPar)
    {
        Console.WriteLine($"{this.GetType().Name}的私有化双参构造函数,参数类型:{sPar.GetType().Name}{iPar.GetType().Name}," +
            $"参数:{sPar}{iPar}");
    }
    #endregion

    #region 方法
    /// <summary>
    /// 无参方法
    /// </summary>
    public void ShowNoPar()
    {
        Console.WriteLine($"{this.GetType().Name}的无参方法,ShowNowPar()");
    }

    public void ShowSinglePar(string par)
    {
        Console.WriteLine($"{this.GetType().Name}的无参方法,参数:{par},参数类型:{par.GetType().Name}");
    }

    public void ShowDoublePar(string sPar, int iPar)
    {
        Console.WriteLine($"{this.GetType().Name}的单参方法,参数:{sPar}{iPar},参数类型:{sPar.GetType().Name}{iPar.GetType().Name}");
    }
    /// <summary>
    /// 重载方法一
    /// </summary>
    public void ShowOverLoad()
    {
        Console.WriteLine($"{this.GetType().Name}的重载方法,无参");
    }
    /// <summary>
    /// 重载方法二
    /// </summary>
    /// <param name="iPar"></param>
    public void ShowOverLoad(int iPar)
    {
        Console.WriteLine($"{this.GetType().Name}的重载方法,参数:{iPar},参数类型:{iPar.GetType().Name}");
    }
    /// <summary>
    /// 重载方法三
    /// </summary>
    /// <param name="sPar"></param>
    public void ShowOverLoad(string sPar)
    {
        Console.WriteLine($"{this.GetType().Name}的重载方法,参数:{sPar},参数类型:{sPar.GetType().Name}");
    }
    /// <summary>
    /// 重载方法四
    /// </summary>
    /// <param name="sPar"></param>
    /// <param name="iPar"></param>
    public void ShowOverLoad(string sPar, int iPar)
    {
        Console.WriteLine($"{this.GetType().Name}的重载方法,参数:{sPar}{iPar},参数类型:{sPar.GetType().Name}{iPar.GetType().Name}");
    }
    /// <summary>
    /// 重载方法五
    /// </summary>
    /// <param name="iPar"></param>
    /// <param name="sPar"></param>
    public void ShowOverLoad(int iPar, string sPar)
    {
        Console.WriteLine($"{this.GetType().Name}的重载方法,参数:{iPar}{sPar},参数类型:{iPar.GetType().Name}{sPar.GetType().Name}");
    }

    private void ShowPrivate(string sPar)
    {
        Console.WriteLine($"{this.GetType().Name}的私有方法,参数:{sPar},参数类型:{sPar.GetType().Name}");
    }

    public static void ShowStatic(string sPar)
    {
        Console.WriteLine($"静态方法,参数:{sPar},参数类型:{sPar.GetType().Name}");
    }
    #endregion
}

反射获取对象、调用方法:

/// <summary>
/// 反射创建泛型对象,调用泛型方法
/// </summary>
public static void RefCallCommon()
{
    Console.WriteLine();
    Console.WriteLine("------------------------------根据不同的构造函数创建对象---------------------------------");
    Assembly assembly1 = Assembly.LoadFrom("Reflection04.dll");
    Type type1 = assembly1.GetType("Reflection04.BasicKnowledge.FuncAndMethod");
    //无参构造函数
    object obj1 = Activator.CreateInstance(type1);
    object obj1_1 = Activator.CreateInstance(type1, new object[0]);
    //单参构造函数
    object obj2 = Activator.CreateInstance(type1, new object[] { 157 });
    object obj3 = Activator.CreateInstance(type1, new object[] { "单参构造函数" });
    //双参构造函数
    object obj5 = Activator.CreateInstance(type1, new object[] { 135, "双参构造函数" });
    //私有化双参构造函数,没弄明白
    //object obj6 = Activator.CreateInstance(type1, true);
    
    Console.WriteLine();

    Console.WriteLine("------------------------------调用不同的方法---------------------------------");
    Assembly assembly2 = Assembly.LoadFrom("Reflection04.dll");
    Type type2 = assembly2.GetType("Reflection04.BasicKnowledge.FuncAndMethod");
    object obj7 = Activator.CreateInstance(type2);

    //无参方法
    MethodInfo showNoPar1 = type2.GetMethod("ShowNoPar");
    MethodInfo showNoPar2 = type2.GetMethod("ShowNoPar", new Type[0]);
    showNoPar1.Invoke(obj7, new object[0]);
    showNoPar2.Invoke(obj7, new object[0]);

    //单参方法
    MethodInfo showSinglePar = type2.GetMethod("ShowSinglePar", new Type[] { typeof(string) });
    showSinglePar.Invoke(obj7, new object[] { "单参方法" });

    //双参方法
    MethodInfo showDoublePar = type2.GetMethod("ShowDoublePar", new Type[] { typeof(string), typeof(int) });
    showDoublePar.Invoke(obj7, new object[] { "双参方法", 722 });

    //重载方法
    MethodInfo showOverLoad1 = type2.GetMethod("ShowOverLoad", new Type[0]);
    showOverLoad1.Invoke(obj7, new object[0]);
    MethodInfo showOverLoad2 = type2.GetMethod("ShowOverLoad", new Type[] { typeof(int) });
    showOverLoad2.Invoke(obj7, new object[] { 942 });
    MethodInfo showOverLoad3 = type2.GetMethod("ShowOverLoad", new Type[] { typeof(string) });
    showOverLoad3.Invoke(obj7, new object[] { "单参string方法" });
    MethodInfo showOverLoad4 = type2.GetMethod("ShowOverLoad", new Type[] { typeof(string), typeof(int) });
    showOverLoad4.Invoke(obj7, new object[] { "双参方法string、int", 753 });
    MethodInfo showOverLoad5 = type2.GetMethod("ShowOverLoad", new Type[] { typeof(int), typeof(string) });
    showOverLoad5.Invoke(obj7, new object[] { 943, "双参方法int、string" });

    //调用私有化方法
    MethodInfo showPrivate = type2.GetMethod("ShowPrivate", BindingFlags.NonPublic | BindingFlags.Instance);
    showPrivate.Invoke(obj7, new object[] { "私有化方法" });

    //调用静态方法
    MethodInfo showStatic = type2.GetMethod("ShowStatic", BindingFlags.Static | BindingFlags.Public);
    showStatic.Invoke(obj7, new object[] { "静态方法" });
}

反射创建对象、调用方法

7、反射调用属性、字段

public class People
{
    public People()
    {
        Console.WriteLine($"{this.GetType().Name}被创建");
    }

    public int Id { get; set; }

    public string Name { get; set; }

    public string Description;
}
/// <summary>
/// 反射调用对象的属性、字段
/// </summary>
public static void RefCallPropField()
{
    Console.WriteLine("-----------------------------反射获取属性、字段--------------------------------");
    //属性:由get、set方法,本质上属性并不能存储值,存储值是由字段完成的,只是属性对应的字段我们并没有显式地写出来
    //字段:没有get、set方法
    Assembly assembly1 = Assembly.LoadFrom("Reflection.Model.dll");
    Type type1 = assembly1.GetType("Reflection.Model.People");
    object oResult = Activator.CreateInstance(type1);
    //遍历对象属性并赋值
    foreach(PropertyInfo prop in type1.GetProperties())
    {
        if (prop.Name.Equals("Id"))
            prop.SetValue(oResult, 138);
        if (prop.Name.Equals("Name"))
            prop.SetValue(oResult, "超超");
        Console.WriteLine($"{type1.Name}的属性:{prop.Name}");
    }
    Console.WriteLine();
    foreach(PropertyInfo prop in type1.GetProperties())
    {
        Console.WriteLine($"{type1.Name}的属性:{prop},属性值:{prop.GetValue(oResult)}");
    }
    Console.WriteLine();
    foreach(FieldInfo fieldInfo in type1.GetFields())
    {
        if (fieldInfo.Name.Equals("Description"))
            fieldInfo.SetValue(oResult, "小舍得最萌人物");
        Console.WriteLine($"{type1.Name}的属性:{fieldInfo.Name}");
    }
    Console.WriteLine();
    foreach(FieldInfo fieldInfo in type1.GetFields())
    {
        Console.WriteLine($"{type1.Name}的字段:{fieldInfo.Name},字段值:{fieldInfo.GetValue(oResult)}");
    }
}

8、反射创建泛型对象,调用泛型方法

public class GenericClass<U,V,W>
{
    public void Show(U u,V v,W w)
    {
        Console.WriteLine($"{this.GetType().Name}的Show()方法,参数:{u}{v}{w},参数类型:{typeof(U)}{typeof(V)}{typeof(W)}");
    }
}

public class GenericMethod
{
    public void Show<U,V,W>(U u,V v,W w)
    {
        Console.WriteLine($"{this.GetType().Name}的Show()方法,参数:{u}{v}{w},参数类型:{typeof(U)}{typeof(V)}{typeof(W)}");
    }
}

public class GenericComplex<U>
{
    public void Show<V,W>(U u,V v,W w)
    {
        Console.WriteLine($"{this.GetType().Name}的Show()方法,参数:{u}{v}{w},参数类型:{typeof(U)}{typeof(V)}{typeof(W)}");
    }
}
/// <summary>
/// 反射创建泛型对象,调用泛型方法
/// </summary>
public static void ShowGeneric()
{
    Console.WriteLine("------------------------------情况一:泛型类型只位于类名上---------------------------------");
    Assembly assembly1 = Assembly.LoadFrom("Reflection04.dll");
    //获取泛型类型时,全类名=命名空间+类名+飘点`n
    Type type1 = assembly1.GetType("Reflection04.BasicKnowledge.GenericClass`3");
    Type type1_1 = type1.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
    object oResult1 = Activator.CreateInstance(type1_1);
    MethodInfo methodInfo1 = type1_1.GetMethod("Show");
    methodInfo1.Invoke(oResult1, new object[] { 843, "田雨岚", DateTime.Now });
    Console.WriteLine();

    Console.WriteLine("------------------------------情况二:泛型类型只位于方法名上---------------------------------");
    Assembly assembly2 = Assembly.LoadFrom("Reflection04.dll");
    Type type2 = assembly2.GetType("Reflection04.BasicKnowledge.GenericMethod");
    object oResult2 = Activator.CreateInstance(type2);
    MethodInfo methodInfo2 = type2.GetMethod("Show");
    MethodInfo methodInfo2_2 = methodInfo2.MakeGenericMethod(new Type[] { typeof(string), typeof(double), typeof(int) });
    methodInfo2_2.Invoke(oResult2, new object[] { "小舍得", 37.215, 953 });
    Console.WriteLine();

    Console.WriteLine("------------------------------情况三:泛型类型既位于类名又位于方法名上---------------------------------");
    Assembly assembly3 = Assembly.LoadFrom("Reflection04.dll");
    Type type3 = assembly3.GetType("Reflection04.BasicKnowledge.GenericComplex`1");
    Type type3_1 = type3.MakeGenericType(new Type[] { typeof(DateTime) });
    object oResult3 = Activator.CreateInstance(type3_1);
    MethodInfo methodInfo3 = type3_1.GetMethod("Show");
    MethodInfo methodInfo3_1 = methodInfo3.MakeGenericMethod(new Type[] { typeof(int), typeof(string) });
    methodInfo3_1.Invoke(oResult3, new object[] { DateTime.Now, 834, "南峰" });
}

9、极简ORM框架

public interface IDBHelper
{
    T Query<T>(int key) where T : BaseModel;
}
public class SqlServerHelper : IDBHelper
{
    public T Query<T>(int key) where T : BaseModel
    {
        Type type = typeof(T);
        object oResult = Activator.CreateInstance(type);

        var propArr = type.GetProperties().Select(p => $"[{p.Name}]").ToArray();
        string props = string.Join(',', propArr);
        string sql = $"select {props} from [{type.Name}] where Id = {key}";

        string sqlStr = ReadConfig.GetConfig("SqlServerConfig");

        using (SqlConnection conn = new SqlConnection(sqlStr))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            conn.Open();
            SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            if (reader.Read())
            {
                foreach(PropertyInfo prop in type.GetProperties())
                {
                    prop.SetValue(oResult, reader[prop.Name]);
                }
            }
            conn.Close();
        }

        return oResult as T;
    }
}
/// <summary>
/// 读取配置文件
/// 1.引入①Microsoft.Extension.Configuration; ②Microsoft.Extension.Configuration.Json
/// 2.将配置文件appsettings.json的属性设置为始终复制
/// </summary>
public class ReadConfig
{
    public static string GetConfig(string key)
    {
        IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
        IConfigurationRoot configurationRoot = builder.Build();
        string config = configurationRoot["ConnectionStrs:" + key];

        return config;
    }
}

极简ORM框架
需要注意的是,这样写的前提是,类的属性名必须与数据库里的字段名完全一致。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值