一. 加载dll,读取相关信息

1. 加载程序集的三种方式

 调用Assembly类下的三个方法:Load、LoadFile、LoadFrom。

        //1.1 Load方法:动态默认加载当前路径下的(bin)下的dll文件,不需要后缀
Assembly assembly = Assembly.Load("DB.SQLServer");
//1.2 LoadFile方法:程序集的绝对路径
Assembly assembly2 = Assembly.LoadFile(@"D:\我的框架之路\DotNet体系\02-DotNet进阶\02-反射\01-code\Reflection\bin\Debug\DB.SQLServer.dll");
//1.3 LoadFrom方法:可以是当前路径(需要写上后缀.dll),也可以是绝对路径
Assembly assembly3 = Assembly.LoadFrom("DB.SQLServer.dll");

2. 获取程序集中所有的类

 通过方法GetTypes来实现。

 Assembly assembly = Assembly.Load("DB.SQLServer");
Type[] t1 = assembly.GetTypes();

通过方法GetType("类名全写")来实现获取单个类(DBHelper)。

 Type tItem = assembly.GetType("DB.SQLServer.DBHelper");

3. 获取类中的所有构造函数

  通过方法GetConstructors来实现。

 //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2.获取程序集中的特定类
Type tItem = assembly.GetType("DB.SQLServer.DBHelper");
//3.获取特定类中的所有构造函数
ConstructorInfo[] cInfor = tItem.GetConstructors();

4. 获取类中的所有属性

 通过方法GetProperties来实现。

  //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2.获取程序集中的特定类
Type tItem = assembly.GetType("DB.SQLServer.DBHelper");
//3.获取特定类中的所有属性
PropertyInfo[] propertyInfo = tItem.GetProperties();

5. 获取类中的所有方法

通过方法GetMethods来实现。

 //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2.获取程序集中的特定类
Type tItem = assembly.GetType("DB.SQLServer.DBHelper");
//3.获取特定类中的所有方法
MethodInfo[] methordInfo = tItem.GetMethods();

6. 获取类中的所有接口

  通过方法GetInterfaces来实现。

 //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2.获取程序集中的特定类
Type tItem = assembly.GetType("DB.SQLServer.DBHelper");
//3.获取特定类中的所有接口
Type[] type = tItem.GetInterfaces();

二. 反射创建对象

1. 反射创建对象

 通过Activator.CreateInstance()方法来创建对象

1.1 ReflectionTest类的代码

  public class ReflectionTest
{
public int Id { get; set; }
public string Name { get; set; } public string Field = null;
public static string FieldStatic = null; #region 构造函数
public ReflectionTest()
{
Console.WriteLine("这里是{0}无参数构造函数", this.GetType());
} public ReflectionTest(string name)
{
Console.WriteLine("这里是{0} 有1个参数构造函数", this.GetType());
} public ReflectionTest(int id, string name)
{
Console.WriteLine("这里是{0} 有2个参数构造函数", this.GetType());
}
#endregion public void Show1()
{
Console.WriteLine("这里是{0}的Show1", this.GetType());
} public void Show2(int id)
{ Console.WriteLine("这里是{0}的Show2", this.GetType());
} public static void ShowStatic(string name)
{
Console.WriteLine("这里是{0}的ShowStatic", typeof(ReflectionTest));
} public void Show3()
{
Console.WriteLine("这里是{0}的Show3_1", this.GetType());
} public void Show3(int id, string name)
{
Console.WriteLine("这里是{0}的Show3", this.GetType());
} public void Show3(string name, int id)
{
Console.WriteLine("这里是{0}的Show3_2", this.GetType());
} public void Show3(int id)
{ Console.WriteLine("这里是{0}的Show3_3", this.GetType());
} public void Show3(string name)
{ Console.WriteLine("这里是{0}的Show3_4", this.GetType());
} private void Show4(string name)
{
Console.WriteLine("这里是{0}的Show4", this.GetType());
}
public void ShowGeneric<T>(T name)
{
Console.WriteLine("这里是{0}的ShowStatic T={1}", this.GetType(), typeof(T));
}
}

1.2 反射创建对象的代码

 //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2.获取程序集中的特定类
Type tItem = assembly.GetType("DB.SQLServer.ReflectionTest");
//3.1 无参构造函数
Activator.CreateInstance(tItem);
//3.2 一个参数的构造函数
Activator.CreateInstance(tItem ,"");
//3.3 两个参数的构造函数
Activator.CreateInstance(tItem , ,"");

2. 反射破坏单例,调用私有构造函数

   单例代码

  public sealed class Singleton
{
private Singleton()
{
Console.WriteLine("初始化一次");
} private static Singleton Instance = new Singleton(); public static Singleton CreateInstance()
{
return Instance;
}
}

   破坏单例,调用私有构造函数代码

 //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2. 获取单例类
Type tc3 = assembly .GetType("DB.SQLServer.Singleton");
//3. 创建对象
Activator.CreateInstance(tc3, true);

3. 反射创建泛型(扩展)

  //1.加载程序集
Assembly assembly = Assembly.Load("DB.SQLServer");
//2. 获取单例类
Type tc4 = assembly4.GetType("DB.SQLServer.GenericClass`1");
tc4 = tc4.MakeGenericType(typeof(int));
Activator.CreateInstance(tc4);

三. IOC(反射+简单工厂+配置文件)

  背景:有三套相同的数据库,分别是SQLServer、MySQL、Oracle数据库,要求可以分别连接这三个不同的数据库,并且发布后,可以在不重新发布的情况下,切换连接数据库。

  对应上述背景,建立相应的解决方案,目录如下,并介绍介绍几种传统的解决方案

方案一:在Reflection中直接添加对DB.SQLServer的引用

  Console.WriteLine("------------------------------------1. 传统方式调用DBHelper中的Query方法--------------------------------------");
//1.传统的方式调用(需要对 DB.SQLServer添加引用)
DBHelper db1 = new DBHelper("");
db1.Query();

方案二:在Reflection中直接添加对DB.SQLServer、DB.Interface的引用

   Console.WriteLine("------------------------------------2. 接口的方式调用--------------------------------------");
//2. 接口的方式调用(只需要引用接口的程序集即可)
IDBHelper idb1 = new DBHelper("");
idb1.Query();

点评:以上两种方案实质上都是通过对相应的实现类添加引用,new出来对象,然后调用方法来实现,没法发布后动态修改数据库。

方案三:通过反射来创建对象,只需要添加对DB.Interface的引用即可,但需要把DB.SQLServer、DB.MySQL、DB.Oracle的生成dll的目录改成Reflection程序下

  Console.WriteLine("------------------------------------3. 反射的方式创建对象--------------------------------------");
//3. 反射的方式创建对象(不需要直接添加对其引用,只需要把相应的程序生成路径改成Reflection中即可)
Assembly assembly4 = Assembly.Load("DB.SQLServer");
Type tc = assembly4.GetType("DB.SQLServer.DBHelper");
//object myDbHelper = Activator.CreateInstance(tc, "123"); //调用带参数的构造函数
object myDbHelper = Activator.CreateInstance(tc); //默认调用无参构造函数
IDBHelper idb2 = (IDBHelper)myDbHelper;
idb2.Query();

点评:该方案只需要对接口添加引用,符合了面向接口编程的思想,但是发布后在不修改代码的情况下,不能切换数据库。

方案四:IOC(反射+简单工厂+配置文件),需要添加对DB.Interface的引用,并且把DB.SQLServer、DB.MySQL、DB.Oracle的生成dll的目录改成Reflection程序下

配置文件:

   <appSettings>
<!--直接修改配置文件,可以修改数据库连接牛逼,可以直接切换 oracle 、mysql数据库,发布后可以直接通过改配置文件,切换数据库,代码什么也不用改,体会:反射+面向接口编程-->
<!--前提:相应的DBHelper类必须满足接口约束,需要把Oracle或MySql的dll文件拷贝到Reflection中的bin文件中 -->
<!--SQLServer改为: -->
<!--<add key="IDBHelper-dllName" value="DB.SQLServer"/>
<add key="IDBHelper-className" value="DB.SQLServer.DBHelper"/>-->
<!--Oracle改为: -->
<!--<add key="IDBHelper-dllName" value="DB.Oracle"/>
<add key="IDBHelper-className" value="DB.Oracle.DBHelper"/>-->
<!--MySql改为: -->
<add key="IDBHelper-dllName" value="DB.MySql"/>
<add key="IDBHelper-className" value="DB.MySql.DBHelper"/>
</appSettings>

简单工厂:

 /// <summary>
/// 简单工厂,创建对象
/// </summary>
public class SimpleFactory
{
private static string IDBHelperdllName = ConfigurationManager.AppSettings["IDBHelper-dllName"];
private static string IDBHelperClassName = ConfigurationManager.AppSettings["IDBHelper-className"]; public static IDBHelper CreateDBHelper()
{
Assembly assembly = Assembly.Load(IDBHelperdllName);
Type type = assembly.GetType(IDBHelperClassName);
object obj = Activator.CreateInstance(type);
return (IDBHelper)obj;
} }

调用:

   Console.WriteLine("------------------------------------4. IOC(反射+简单工厂+配置文件)--------------------------------------");
//4. IOC(反射+简单工厂+配置文件)(不需要直接添加对其引用,只需要把相应的程序生成路径改成Reflection中即可)
IDBHelper idb3 = SimpleFactory.CreateDBHelper();
idb3.Query();

四. 反射调用实例方法、静态方法、重载方法

         //2. 实例方法、静态方法、重载方法的调用
{
object obj = Activator.CreateInstance(tc5);
Console.WriteLine("---------------------------------2.1 调用无参、有参的实例方法-------------------------------------");
//2.1 调用无参、有参的实例方法
{
MethodInfo methord = tc5.GetMethod("Show1");
methord.Invoke(obj, null);
}
{
MethodInfo methord = tc5.GetMethod("Show2");
methord.Invoke(obj, new object[]{});
} Console.WriteLine("---------------------------------2.2 调用静态方法-------------------------------------");
//2.2 调用静态方法
{
MethodInfo methord = tc5.GetMethod("ShowStatic");
methord.Invoke(obj, new object[] { "ShowStatic1234" });
} Console.WriteLine("---------------------------------2.3 调用重载方法(需要在创建方法的时候,把类型传进去)-------------------------------------");
//2.3 调用重载方法(需要在创建方法的时候,把类型传进去)
{
MethodInfo methord = tc5.GetMethod("Show3", new Type[] { });
methord.Invoke(obj, null);
}
{
MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(int)});
methord.Invoke(obj, new object[] { });
}
{
MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(string) });
methord.Invoke(obj, new object[] { "" });
}
{
MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(int), typeof(string) });
methord.Invoke(obj, new object[] { ,""});
}
{
MethodInfo methord = tc5.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
methord.Invoke(obj, new object[] { "", });
} Console.WriteLine("---------------------------------2.4 调用私有方法-------------------------------------");
//2.4 调用私有方法
{
MethodInfo methord = tc5.GetMethod("Show4", BindingFlags.Instance|BindingFlags.NonPublic);
methord.Invoke(obj, new object[] { "" });
}
Console.WriteLine("---------------------------------2.5 调用泛型方法-------------------------------------");
//2.5 调用泛型方法
{
MethodInfo methord = tc5.GetMethod("ShowGeneric");
methord = methord.MakeGenericMethod(typeof(string));
methord.Invoke(obj, new object[] { "" });
} }

五. 反射字段和属性,获取值和设置值

  {
//实例化对象
ReflectionTest rTest = new ReflectionTest();
//反射
Assembly assembly5 = Assembly.Load("DB.SQLServer");
Type type5 = assembly5.GetType("DB.SQLServer.ReflectionTest");
object object5 = Activator.CreateInstance(type5);
//1.获取类的所有属性
Console.WriteLine("------------------------------------1.获取类的属性--------------------------------------");
var pInfor= type5.GetProperties();
foreach (var item in pInfor)
{
Console.WriteLine(item.Name);
if (item.Name.Equals("Id"))
{
item.SetValue(object5, );
}
}
Console.WriteLine("------------------------------------输出ID属性值--------------------------------------");
rTest = (ReflectionTest)object5;
Console.WriteLine(rTest.Id);
//2.获取类的字段
Console.WriteLine("------------------------------------2.获取类的字段--------------------------------------");
foreach (var item in type5.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
{
Console.WriteLine(item.Name);
} }

六. 反射的好处和局限

  {
//反射的好处:
//反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。
//有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。 //性能局限
Console.WriteLine("------------------------------测试普通方法和反射的耗时情况--------------------------------------");
{
//1.普通方法
Stopwatch watch = new Stopwatch();
watch.Start();
for (var i = ; i < ; i++)
{
DBHelper2 dh = new DBHelper2();
dh.Id = ;
dh.Name = "maru";
dh.Query();
}
watch.Stop();
Console.WriteLine("普通方式花费{0}ms:",watch.ElapsedMilliseconds);
}
{
//2. 反射的方法
Stopwatch watch = new Stopwatch();
watch.Start();
Assembly assembley6 = Assembly.Load("DB.SQLServer");
Type type6 = assembley6.GetType("DB.SQLServer.DBHelper2");
for (var i = ; i < ; i++)
{
object obj6 = Activator.CreateInstance(type6);
foreach (var item in type6.GetProperties())
{
if (item.Name.Equals("Id"))
{
item.SetValue(obj6, );
}
if (item.Name.Equals("Name"))
{
item.SetValue(obj6, "maru");
}
}
MethodInfo m6 = type6.GetMethod("Query");
m6.Invoke(obj6, null);
}
watch.Stop();
Console.WriteLine("反射方式花费{0}ms:", watch.ElapsedMilliseconds);
}
}

运行结果:

 七. 补充:获取类的属性、方法、特性、构造函数等,设置属性值,获取属性值

函数                                                                         说明
GetConstructor(s)                  取得此类型的创建函数,其将回传一个ConstructorInfo对象或数组
GetField(s)                             取得此类型中成员变量,其将回传一个FiledInfo对象或数组
GetMember(s)                        取得此类中的成员,其类型可以是变量、事件、属性、方法及Nested Type,其将回传一个MemberInfo对象或数组
GetEvent(s)                            取得此类型中的事件,其将回传一个EventInfo对象或数组
GetProperty/GetProperties         取得此类型中的属性,其将回传一个PropertyInfo对象或数组
GetNestedType(s)                  取得声明于此类型内类型,其将回传一个Type对象或数组
GetCustomAttibutes                    取得绑定于此类型的Attitudes
GetValue(t)                                  获取t对象的的属性值
SetValue(t,"XXX")                        设置t对象的属性值为XXX

实体代码:

  [Description("我是Person类")]
public class Person
{
//1. 构造函数
public Person()
{ }
public Person(string sex)
{
this._Sex = sex;
}
//2. 属性 [Description("我是id")]
public string id { get; set; } [ReadOnly(true)]
public string userName { get; set; } public string userPwd { get; set; } //3.成员变量 public string _Sex = null; public int _Age; //4. 特性 [Description("我是id")] [ReadOnly(true)] //5. 方法
public string GetOwnSex()
{
return this._Sex;
} //6. 事件
public event Action MyEvent; }

调用代码:

  Person person1 = new Person()
{
id = "",
userName = "ypf",
userPwd = "",
};
//获取类
Type type = person1.GetType();
{
Console.WriteLine("---------------1. 获取构造函数-----------------");
ConstructorInfo[] constructorInfoList = type.GetConstructors();
for (int i = ; i < constructorInfoList.Length; i++)
{
Console.WriteLine(constructorInfoList[i]);
}
}
{
Console.WriteLine("---------------2. 获取成员变量-----------------");
FieldInfo[] fielInforList = type.GetFields();
for (int i = ; i < fielInforList.Length; i++)
{
Console.WriteLine(fielInforList[i]);
}
}
{
Console.WriteLine("---------------3. 获取成员-----------------");
MemberInfo[] memberInfoList = type.GetMembers();
for (int i = ; i < memberInfoList.Length; i++)
{
Console.WriteLine(memberInfoList[i]);
}
}
{
Console.WriteLine("---------------4. 获取事件-----------------");
EventInfo[] eventInfoList = type.GetEvents();
for (int i = ; i < eventInfoList.Length; i++)
{
Console.WriteLine(eventInfoList[i]);
}
}
{
Console.WriteLine("---------------5. 获取属性-----------------");
PropertyInfo[] propertyInfoList = type.GetProperties();
for (int i = ; i < propertyInfoList.Length; i++)
{
Console.WriteLine(propertyInfoList[i]);
}
}
{
Console.WriteLine("---------------6. 获取特性-----------------");
//1. 获取属性上的特性
//因为这些测试所用的特性都是加在属性上的,所以要先获取属性
PropertyInfo[] propertyInfoList = type.GetProperties();
foreach (var item in propertyInfoList)
{
//获取该属性上的所有特性
object[] attributeInfoList = item.GetCustomAttributes(true);
foreach (var item2 in attributeInfoList)
{
Console.WriteLine("{0}属性上的特性为{1}", item, item2);
} }
//2. 获取类上的属性
object[] attributeInfoList2 = type.GetCustomAttributes(true);
foreach (var item3 in attributeInfoList2)
{
Console.WriteLine("{0}类上的特性为{1}", type, item3);
}
}
{
Console.WriteLine("---------------7. 获取id属性的值-----------------");
PropertyInfo idProperty = type.GetProperty("id");
Console.WriteLine("属性名为:{0}", idProperty.Name);
Console.WriteLine("属性值为:{0}", idProperty.GetValue(person1));
//设置属性值
idProperty.SetValue(person1, "");
Console.WriteLine("设置后的属性值为:{0}", idProperty.GetValue(person1)); }

结果:

第六节:反射(几种写法、好处和弊端、利用反射实现IOC)的更多相关文章

  1. 风炫安全web安全学习第三十六节课-15种上传漏洞讲解(一)

    风炫安全web安全学习第三十六节课 15种上传漏洞讲解(一) 文件上传漏洞 0x01 漏洞描述和原理 文件上传漏洞可以说是日常渗透测试用得最多的一个漏洞,因为用它获得服务器权限最快最直接.但是想真正把 ...

  2. [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦

    [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...

  3. MYSQL 之 JDBC(六): 增删改查(四)利用反射及JDBC元数据编写通用的查询

    1.先利用SQL进行查询,得到结果集2.利用反射创建实体类的对象:创建Student对象3.获取结果集的列的别名:idCard.studentName4.再获取结果集的每一列的值,结合3得到一个Map ...

  4. [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...

  5. java设计模——反射的应用 (利用反射来去除if判断语句)

    利用反射来去除if判断语句 我的以前写的一个查分系统,就是部长让我写的那个,使用一个分发器(函数),他会根据传递进来的字符串参数调用不同的方. If(“add”.equalsIgnoreCase(fu ...

  6. 【java】工厂模式Factory,利用反射改进

    package 反射; interface Product{ public void produce(); } class socks implements Product{ @Override pu ...

  7. 利用反射修改final数据域

    当final修饰一个数据域时,意义是声明该数据域是最终的,不可修改的.常见的使用场景就是eclipse自动生成的serialVersionUID一般都是final的. 另外还可以构造线程安全(thre ...

  8. ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪

    ASP.NET MVC深入浅出(被替换)   一. 谈情怀-ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态 ...

  9. 第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联

    第九节: 利用RemoteScheduler实现Sheduler的远程控制   一. RemoteScheduler远程控制 1. 背景: 在A服务器上部署了一个Scheduler,我们想在B服务器上 ...

  10. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

随机推荐

  1. 含有package关键字的java文件在命令行运行报错

    程序中含有package关键字,使用命令行运行程序时出现"找不到或无法加载主类",而使用Eclipse软件可以正常运行程序的可能解决办法. 在包下的类,在Java源文件的地方编译后 ...

  2. XML详解二XML的解析与创建

    XML用来传输和存储数据,如何解析获取到的XML文本呢? 一.解析XML 创建demo.xml文件: <?xml version="1.0" encoding="U ...

  3. LeetCode算法题-Design LinkedList(Java实现)

    这是悦乐书的第300次更新,第319篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第168题(顺位题号是707).设计链表的实现.您可以选择使用单链表或双链表.单链表中的 ...

  4. Spring Boot 正常启动后访问Controller提示404

    问题描述 今天重新在搭建Spring Boot项目的时候遇到访问Controller报404错误,之前在搭建的时候没怎么注意这块.新创建项目成功后,作为项目启动类的Application在com.bl ...

  5. 倒计时js

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. java 下载word freemaker

    网上有很多优质的博文了,这里这篇博客就是记录一下字自己,写demo的历程,坑和收获 在java程序中下载word 有6中方式,此处省略(嘻嘻),不过大家公认的是 freemaker 和 PageOff ...

  7. 线性回归和Logistic回归

    目录 线性回归 用线性回归模型拟合非线性关系 梯度下降法 最小二乘法 线性回归用于分类(logistic regression,LR) 目标函数 如何求解\(\theta\) LR处理多分类问题 线性 ...

  8. 小议SQL数据插入

    --数据插入操作:INSERT INTO user_info(username,age) VALUES('ZHANGSAN',20);INSERT INTO user_info(username,ph ...

  9. rsync用法详细解释

    提要 熟悉 rsync 的功能及其特点 掌握 rsync 语法及常用选项的功能 掌握 rsync 命令的三种基本使用方法 掌握如何筛选 rsync 的传输目标 掌握使用 rsync 进行镜像和增量备份 ...

  10. Shell命令-文件及内容处理之diff、vimdiff

    文件及内容处理 - diff.vimdiff 1. diff:比较两个文件区别 diff命令的功能说明 diff命令用比较文件的差异.diff以逐行的方式,比较文本文件的异同处.如果指定要比较目录,则 ...