C#语法——反射,架构师的入门基础。

前言

编程其实就是写代码,而写代码目的就是实现业务,所以,语法和框架也是为了实现业务而存在的。因此,不管多么高大上的目标,实质上都是业务。

所以,我认为不要把写代码上升到科学的高度。上升到艺术就可以了,因为艺术本身也没有高度。。。。

软件设计存在过度设计,语法和框架的理解,也存在过度理解。比如,反编译下,看看反射是怎么实现的。。。

有兴趣是好事,但就算知道了反射的本质,了解了反射是如何设计的,你技术也没什么质的改变。因为,技术水平最终还是要落实到应用上。

在比如,过度的追求代码性能,也不见得是一件好事,因为,[大多数]情况下,硬件比程序员便宜多了。。。(注意这里指的是代码不是算法和数据库性能)

所以,不论什么事,过度了,总不是好事。

----------------------------------------------------------------------------------------------------

本篇文章主要介绍C#反射【用法】。

反射是架构师必会的基础,因为任何一个被设计出来的框架,都要使用反射。

反射也是最隐蔽的语法,因为反射写出来后,通常它会被直接封装,然后调用者就只负责使用,不再关注他的具体实现。

这与它的特性有关,因为反射就是为了减少代码冗余而存在的,所以,看不见很正常。

反射的定义

官方定义:反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。

看不懂?没关系,我们把它翻译成人类可理解的语言。

C#编程语言中,最常使用的是类和类中的函数和属性。正向调用的方法是,创建类,然后用类创建一个对象。接下来就可以用这个对象调用类中的方法和属性了。

而反射,就是相对于这种正向调用的存在。即,它是反向调用。

反射可以通过类名的字符串来创建类,可以通过函数名的字符串和属性名的字符串,来调用类下的函数和属性。

有同学会问了, 既然正向可以调用,那么反向调用干什么呢?

会有这种问题的同学,先别着急,继续往下看,反射既然存在,就必然有存在的道理。

反射的基础应用

1,类反射

先看下面代码;代码为通过类名称的字符,反射出类的对象。

public class ReflectionSyntax
{
    public static void Excute()
    {
        Type type = GetType("Syntax.Kiba");
        Kiba kiba = (Kiba)Activator.CreateInstance(type);
        Type type2 = GetType2("Syntax.Kiba");
        Kiba kiba2 = (Kiba)Activator.CreateInstance(type2);
    }
    public static Type GetType(string fullName)
    {
        Assembly assembly = Assembly.Load("Syntax");
        Type type = assembly.GetType(fullName, true, false);
        return type;
    }

    public static Type GetType2(string fullName)
    {
        Type t = Type.GetType(fullName);
        return t;
    }
}
public class Kiba
{
    public void PrintName()
    {
        Console.WriteLine("Kiba518");
    }
}

在代码中我们看到,反射时传递了字符串"Syntax.Kiba",然后通过解析字符串,获取到了该字符串对应的类的类型,最后再借助Activator来辅助创建类的实例。

其中字符串"Syntax.Kiba"是一个完全限定名。什么是完全限定名?完全限定名就是命名空间+类名。在反射的时候,需要我们传递完全限定名来确定到底要去哪个命名空间,找哪个类。

在代码中我们还可以看到,获取类型的方式有两种,一种是较复杂的,一种是简单的。

GetType2方法是简单的获取类别,通过Type直接就解析了字符串。而GetType则先进行了加载Assembly(组件),然后再由组件获取类型。

两者有什么区别呢?

区别是,用Type直接解析,只能解析当前命名空间下的类。如果该类存在于引用的DLL中,就解析不了。

而GetType方法中的[Assembly.Load指定了程序集名],所以,在反射时,就会去指定的命名空间里找对应的类。这样就能找到非本程序集下的类了。

[Assembly.Load指定了程序集名]这句话不好理解?

没关系,换个表达,Assembly.Load指定了命名空间的名称,所以反射时,会去这个命名空间里找类,这样是不是就好理解了。

Assembly

Assembly的存在让反射变得特别灵活,其中Assembly.Load不止可以导入我们引入的程序集(或命名空间)。

也可以导入我们未引入程序集的dll。调用模式如下:

System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");

Assembly导入了程序集后,还可以不借助Activator来辅助,自己就可以创建类。如下:

Assembly assembly = Assembly.Load("Syntax");
Kiba kiba = (Kiba)assembly.CreateInstance("Syntax.Kiba");

有的同学可能会担心性能,会觉得这样反射,会使程序变慢。

有这种想法的同学,其实你已经是在过度理解语法了。这种地方的代码性能其实是可以不用关心的。

那么,到底会不会变慢呢?

答案是这样的,如果你是使用完全限定名来反射,速度就是一样的。如果是反射时,只写了一个类名,那么速度就会变慢。因为它要遍历所有的命名空间,去找这个类。

即,只要反射时把类的命名空间写全,那么速度就不会慢。

2,函数反射

函数的反射应用主要是使用类MethodInfo类反射,下面先看下基础应用。

public static void ExcuteMethod()
{
    Assembly assembly = Assembly.Load("Syntax");
    Type type = assembly.GetType("Syntax.Kiba", true, false);
    MethodInfo method =  type.GetMethod("PrintName");
    object kiba = assembly.CreateInstance("Syntax.Kiba");
    object[] pmts = new object[] { "Kiba518" };
    method.Invoke(kiba, pmts);//执行方法
}
public class Kiba
{
    public string Name { get; set; }
    public void PrintName(string name)
    {
        Console.WriteLine(name);
    }
}

一些同学第一眼看上去可能会有点不适应,因为好像很多类都是大家不经常用的。这也没办法,因为这是一个进阶的过程,必须经历从陌生到熟悉。当你熟悉了这样的代码后,就代表你的技术水平又进步了一个台阶。

下面讲解一些这些代码。

首先我们导入了命名空间,接着我们获取了该命名空间下Kiba这个类的类型;接下来我们通过这个类型来获取指定名称的函数。

然后我们通过Assembly创建了一个Kiba的实例,接着定义了一个参数的Object数组,因为Kiba类下的函数PrintName只有一个参数,所以,我们只为这个Object数组添加一个对象[Kiba518]。

最后,我们通过method.Invoke来调用这个函数,由于是反射,所以调用时,需要指定Kiba类的实例对象和入参。

这样,函数的反射就实现了。

3,属性反射

属性反射是用PropertyInfo类来实现,下面看基础的属性反射。

public static void ExcuteProperty()
{
    Kiba kiba = new Kiba();
    kiba.Name = "Kiba518";
    object name = ReflectionSyntax.GetPropertyValue(kiba, "Name");
    Console.WriteLine(name);
}
public static object GetPropertyValue(object obj, string name)
{
    PropertyInfo property = obj.GetType().GetProperty(name);
    if (property != null)
    {
        object drv1 = property.GetValue(obj, null);
        return drv1;
    }
    else
    {
        return null;
    }
}

如代码所示,首先我们定义了一个Kiba的对象,并为Name赋值,然后我们通过GetPropertyValue方法,传递了Kiba对象和要获取值的属性名称。

GetPropertyValue函数里通过使用PropertyInfo完成了反射。

有的同学可能会觉得,这个很鸡肋,既然已经得到对象,还反射做什么,直接获取就可以了呀。

别着急,我们接下来一起看反射的架构应用。

反射的架构应用

框架编写的核心目的之一,是统一系统秩序。那么什么是系统秩序呢?

首先我们看下系统的构成,系统个通常是由子系统,程序集,类,函数这四部分构成。如下图所示。

既然系统由子系统,程序集,类,函数这四个基础元素构成,那么系统秩序,自然指的就是这四个元素的秩序。而这四个元素最难形成秩序的就是函数了。

很显然,任何的项目都存在重复的函数,或者功能相近的函数。而彻底杜绝这种情况,显然是不可能的。那么我们只好尽量是设计会避免重复元素的框架了。而反射,正是为此而存在的。

反射的架构应用

现实中的框架因为这样那样的原因,会有千奇百怪的设计,所以拘泥于一种设计模式是愚蠢的,实战中要多种设计模式一起应用,局部设计有时候只取设计模式中一部分也可以。这样才能实现项目的量身定制。

所以,这里只介绍一种实战的架构应用,一种使用反射的框架基础结构。下面请框架基础代码。

public class Client
{
    public void ExcuteGetNameCommand()
    {
        Proxy proxy = new Proxy();
        GetNameCommand cmd = new GetNameCommand();
        ResultBase rb = proxy.ExcuteCommand(cmd);
    }
}
public class Proxy
{
    public ResultBase ExcuteCommand(CommandBase command)
    {
        var result = HandlerSwitcher.Excute(command);
        return result as ResultBase;
    }
}
public class HandlerSwitcher
{
    private const string methodName = "Excute";//约定的方法名
    private const string classNamePostfix = "Handler";//约定的处理Command的类的名称的后缀
    //获取命名空间的名称
    public static string GetNameSpace(CommandBase command)
    {
        Type commandType = command.GetType();//获取完全限定名
        string[] CommandTypeNames = commandType.ToString().Split(‘.‘);
        string nameSpace = "";
        for (int i = 0; i < CommandTypeNames.Length - 1; i++)
        {
            nameSpace += CommandTypeNames[i];
            if (i < CommandTypeNames.Length - 2)
            {
                nameSpace += ".";
            }
        }
        return nameSpace;
    }

    public static object Excute(CommandBase command)
    {
        string fullName = command.GetType().FullName;//完全限定名
        string nameSpace = GetNameSpace(command);//命名空间
        Assembly assembly = Assembly.Load(nameSpace);
        Type handlerType = assembly.GetType(fullName + classNamePostfix, true, false);
        object obj = assembly.CreateInstance(fullName + classNamePostfix);
        MethodInfo handleMethod = handlerType.GetMethod(methodName);//获取函数基本信息
        object[] pmts = new object[] { command }; //传递一个参数command
        try
        {
            return handleMethod.Invoke(obj, pmts);
        }
        catch (TargetInvocationException tie)
        {
            throw tie.InnerException;
        }
    }
}
public class GetNameCommandHandler
{
    public ResultBase Excute(CommandBase cmd)
    {
        GetNameCommand command = (GetNameCommand)cmd;
        ResultBase result = new ResultBase();
        result.Message = "I‘m Kiba518";
        return result;
    }
}
public class GetNameCommand: CommandBase
{
}
public class CommandBase
{
    public int UserId { get; set; } 

    public string UserName { get; set; } 

    public string ArgIP { get; set; }
}
public class ResultBase
{
    public string Message { get; set; }
}

代码中框架很简单,主要目的是实现一个代理,用于处理继承了CommandBase的类的代理。

即,客户端,不论传来什么样的Command,只要它是继承自CommandBase的,这个代理都会找到对应的处理类,并执行处理,且返回结果。

为了更清晰的理解这段代码,我们可以参考下面这个流程图。结合了图片在来看代码,框架结构就会更清晰。

这个简单的框架中,使用了一个概念,叫做约定优先原则,也叫做约定优于配置;喜欢概念的小伙伴可以自行百度。

框架中使用的两个约定如下:

第一个是,处理Command的类必须后缀名是Command的类名+Handler结尾。

第二个是,处理Command的类中的处理函数名必须为Excute。

其实概念就是供大家使用的,会用即可;学习的过程中,概念之类的术语,有个印象即可。

PS:为了阅读方便,这里面的类都集中写在了一个命名空间之下了,如果有想使用这种设计模式的同学,请按照自己项目所需进行扩展。

----------------------------------------------------------------------------------------------------

这样,我们就通过反射实现了一个非常简约的框架,通过使用这个框架,会让代码变的更加简洁。

而为了实现每个模块的简洁,反射也将会被封装在各个模块的底层,所以,反射毫无疑问,就是框架设计的基础。

反射与特性

反射在系统中另一个重要应用就是与特性的结合使用。

在一些相对复杂的系统中,难免会遇到一些场景,要讲对象中的一部分属性清空,或者要获取对象中的某些属性赋值。通常我们的实现方式就是手写,一个一个的赋值。

而利用反射并结合特性,完全可以简化这种复杂操作的代码量。

 public partial class ReflectionSyntax
 {
     public void ExcuteKibaAttribute()
     {
         Kiba kiba = new Kiba();
         kiba.ClearName = "Kiba518";
         kiba.NoClearName = "Kiba518";
         kiba.NormalName = "Kiba518";
         ClearKibaAttribute(kiba);
         Console.WriteLine(kiba.ClearName);
         Console.WriteLine(kiba.NoClearName);
         Console.WriteLine(kiba.NormalName);
     }
     public void ClearKibaAttribute(Kiba kiba)
     {
         List<PropertyInfo> plist = typeof(Kiba).GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).ToList();//只获取Public的属性
         foreach (PropertyInfo pinfo in plist)
         {
             var attrs = pinfo.GetCustomAttributes(typeof(KibaAttribute), false);
             if (null != attrs && attrs.Length > 0)
             {
                 var des = ((KibaAttribute)attrs[0]).Description;
                 if (des == "Clear")
                 {
                     pinfo.SetValue(kiba, null);
                 }
             }
         }
     }
 }
 public class Kiba
 {
     [KibaAttribute("Clear")]
     public string ClearName { get; set; }
     [KibaAttribute("NoClear")]
     public string NoClearName { get; set; }
     public string NormalName { get; set; }

 }
 [System.AttributeUsage(System.AttributeTargets.All)]
 public class KibaAttribute : System.Attribute
 {
     public string Description { get; set; }
     public KibaAttribute(string description)
     {
         this.Description = description;
     }
 }

如上述代码所示, 我们通过反射,将拥有KibaAttribute特性的,且描述为Clear的属性,清空了。

当然为了一个属性这么做不值得,但如果一个对象有70个属性的时候,这么做就值得了。

既然能清除属性的数据,那么自然就可以为属性赋值。至于如何实现反射赋值,相信大家可以举一反三。

反射+特性最常见的场景

反射+特性一起应用,最常见的场景就是用ADO.NET从数据库查询出DataTable的数据,然后将DataTable的数据转换成Model实体类型。

我们在开发中,为了让实体更加充血,往往会对数据实体增加一些属性和方法。(什么是充血?充血就是充血模型,有兴趣的同学可以自行百度了解下,简单说就是为实体加属性和方法。)

那么,在用反射,将DataTable转存到Model实体的时候,遍历属性并赋值的时候,就会多遍历那么几次。

如果只是一个实体,那么,多遍历几次也没影响。但,如果是数十万的数据,那这多几次的遍历影响就大了。

而用反射+特性,就可以减少这些额外遍历次数。

讲了这么多为什么不给代码呢?

因为我觉得,将上面的内容全理解的同学,应该可以说,已经框架启蒙了。那么,这个反射+特性的DataTable转数据实体,如果能自己写出来,就算是框架入门了。所以,这里给大家留下了一个练习的空间。

注意,我这里说的是框架,而不是架构。

框架与架构的区别是这样的,框架是个名词,而架构是个动词。框架即便很熟练了,也不见得可以架构的很好。这个大家还是要注意区别。

结语

看完了整篇文章,有的同学可能会有疑问,这么生疏的PropertyInfo和MethodInfo真的有人会用吗?都是Copy代码,然后使用吧。

答案是,当然有人可以熟练应用。反射是架构师的入门基础,任何一个[可以实战]的架构师,都需要随时随地的可以手写出反射,因为优化框架是他们的责任。

所以,对此有所怀疑的小伙伴,可以努力练习了,将委托融入血液,是高级软件工程师的基础,而将反射融入血液,就是架构师的基础了。

C#语法——元组类型

C#语法——泛型的多种应用

C#语法——await与async的正确打开方式

C#语法——委托,架构的血液

C#语法——事件,逐渐边缘化的大哥。

C#语法——消息,MVVM的核心技术。

我对C#的认知。

----------------------------------------------------------------------------------------------------

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的【推荐】,非常感谢!

如果您觉得这篇文章对您有所帮助,那就不妨支付宝小小打赏一下吧。

原文地址:https://www.cnblogs.com/kiba/p/9446905.html

时间: 2024-10-10 16:23:39

C#语法——反射,架构师的入门基础。的相关文章

架构师内练基础

1 JAVA架构师 101.1 SSH文档 101.2 UML 101.3 设计模式 111.4 多线程 111.5 加密算法 121.6 服务器缓存 121.7 LINUX 131.8 J2EE组件 131.9 性能优化 141.10 遗留问题 141.11 名词解释 14 1 JAVA架构师1.1 SSH文档http://wenku.baidu.com/view/89540abd960590c69ec3767f.html  spring2.5http://wenku.baidu.com/vi

系统架构师-基础到企业应用架构-系列索引

系统架构师-基础到企业应用架构-索引 系统架构师-基础到企业应用架构系列会从,系统架构的起源.发展.架构师必备的基础知识与技能.如何把架构应用到企业应用中去.整个系列计划30篇左右,每 一篇都是自己在系统架构过程中的总结和经验,每一篇我都会抱着认真的态度去完成,宁缺毋滥的原则.希望本系列看完之后不但能够帮助看过这个系列的人对系统架 构有深刻的认识,并且能够掌握系统架构中的必备知识,应用到自己的工作中去,更可以共同提高大家的个人能力.本系列希望能够抛砖引玉,希望大家能够多提出宝 贵意见. 前篇 1

Java架构师之路:从Java码农到年薪八十万的架构师,最牛Java架构师进阶路线

从Java码农到年薪八十万的架构师,资深架构师大牛给予Java技术提升学习路线建议,如何成为一名资深Java架构师? 对于工作多年的程序员而言,日后的职业发展无非是继续专精技术.转型管理和晋升架构师三种选择.架构师在一家公司有多重要.优秀架构师需要具备怎样的素质以及架构师的发展现状三个方面来分析 程序员如何才能晋升为优秀的高薪架构师? 希望通过本文让程序员们了解架构师的市场行情,了解架构师的发展前景,并帮助你更清晰地做出职业规划. 架构师在一家公司有多重要 架构师在公司中担当着「IT架构灵魂人物

【JAVA进阶架构师指南】之一:如何进行架构设计

前言 ??本博客是长篇系列博客,旨在帮助想提升自己,突破技术瓶颈,但又苦于不知道如何进行系统学习从而提升自己的童鞋.笔者假设读者具有3-5年开发经验,java基础扎实,想突破自己的技术瓶颈,成为一位优秀的架构师,所谓java基础扎实,比如: ??1.java语言三大特性. ??2.java语言八大基本类型及其表示范围. ??3.为什么float和double存在精度丢失? ??4.publish/private/default/protected表示的范围? ??5.static/final的用

漫画:什么是架构师?

于是,小灰去向大黄请教       这是有关未来的故事: 从前,有一个赶路的人路过一片工地,看到三个年轻人在工地上搬砖. 于是,他问其中一个人: 于是,他又问了第二个人:     于是,他又问了第三个人:     十年之后~ 曾经说自己在建造城市的年轻人,成为了市长. 曾经说自己在搬砖的年轻人,成为了砖厂老板. 曾经说自己在搭建教堂的年轻人,最没出息,成为了架构师. 什么是架构师?   架构师英文architect,这个词源于建筑学.软件工程当中的架构师和建筑工程当中建筑师有许多相通之处,都是负

大数据架构师基础:hadoop家族,Cloudera产品系列等各种技术

大数据我们都知道hadoop,可是还会各种各样的技术进入我们的视野:Spark,Storm,impala,让我们都反映不过来.为了能够更好的架构大数据项目,这里整理一下,供技术人员,项目经理,架构师选择合适的技术,了解大数据各种技术之间的关系,选择合适的语言. 我们可以带着下面问题来阅读本文章: 1.hadoop都包含什么技术 2.Cloudera公司与hadoop的关系是什么,都有什么产品,产品有什么特性 3. Spark与hadoop的关联是什么? 4. Storm与hadoop的关联是什么

程序员到架构师需要的编程基础

程序员到架构师的进阶之路是非常艰辛和漫长的,不但需要掌握很多高级的知识技能,还需要有过硬的基础知识.<Java架构师指南>就是这样一本指导小白到架构师进阶的书.本文摘取了这本书中的第一章节,主要介绍Java程序员走向架构师的基础知识,还有开发环境的搭建.通过本文的学习,可以大致了解程序员的进阶之路,也可更加深刻地认识到程序员的发展方向. 点此链接购买纸书 本书特别适合Java Web领域的开发人员以及刚步入职场的新手.本书通过讲述Java架构师必备的知识技能,让广大读者在原有知识的基础上更上一

系统架构师-基础到企业应用架构-企业应用架构

一.上篇回顾 我们先来回顾下上篇讲解的内容,我们前面的几节分别讲述了,业务逻辑层.数据访问层.服务层.表现层,我们了解了这些分层的职责和分层之间的大概的关联 关系,本篇可能主要是简单的介绍下企业应用的几类模式,结合这几个分层直接的交互来完成系统功能的构建.我们还是先对我们学习的四个分层的职责和功能做个大 概的回顾,我们先来看看下图来回顾下我们讲述的内容. 我想通过上图,大家能回忆起我们讲述的相关内容,然后整理好自己的思路,我们本文将会针对这几个分层进行相应的模式的讲解,并且会结合实例来说明企业应

系统架构师-基础到企业应用架构-数据访问层

一.上章回顾 上篇我们简单讲述了服务层架构模式中的几种,并且讲解了服务层的作用及相关的设计规范,其实我们应该知道,在业务逻辑层中使用领域模型中使用服务层才 能发挥出最大的优势,如果说我们在业务逻辑层还是使用非领域模型的模式话,服务层的作用仅体现在解耦作用.其实在业务逻辑层采用领域模型时,我们前面说的持 久化透明的技术,其实我们可以通过服务层来做,我们在服务层中处理领域对象信息的持久化操作.当然本篇可能不会深入讨论持久化透明的具体实现,后面会单独开 篇来讲述,我们先来回顾下上篇讲解的内容:  上图