2019独角兽企业重金招聘Python工程师标准>>>

尽管C#和C++在语法上有很多相似之处,但C#是更高一级的编程语言,它提供了很多只有高级语言才有的特性,比如属性,委托和事件,这些都是在C#中经常用到的语言特征。

属性

属性(Property) 是类(class)、结构(structure)和接口(interface)的命名(named)成员。类或结构中的成员变量或方法称为 域(Field)。属性(Property)是域(Field)的扩展,且可使用相同的语法来访问。它们使用 访问器(accessors) 让私有域的值可被读写或操作。
属性(Property)不会确定存储位置。相反,它们具有可读写或计算它们值的 访问器(accessors)。例如,有一个名为 Student 的类,带有 age、name 和 code 的私有域。我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。
属性(Property)的访问器(accessor)包含有助于获取(读取或计算)或设置(写入)属性的可执行语句。访问器(accessor)声明可包含一个 get 访问器、一个 set 访问器,或者同时包含二者。
在VS2015中添加属性很简单,只要定义好私有成员后,在成员上定位输入点,按下Ctrl+.选择添加属性即可。很多的快捷函方式都可以通过按下Ctrl+.来实现。
抽象类也可以具有属性,称之为抽象属性,这些属性应在派生类中被实现。

下面一个示例很好的演示了属性的声明,使用以及虚属性继承和实现:

    class Animal{//声明两个私有成员private string name = "Dolly";private int age = 12;//使用属性访问其私有成员public string Name{get{return name;}set{name = value;}}//仅开放get属性,禁止使用set改变成员值public int Age{get{return age;}}//定义自动属性,不创建成员变量public virtual int Cost{get;set;}}class Dog : Animal{private int money;private int cost;public int Money{get{return money;}set{money = value;}}//实现基类的属性public override int Cost{get{return cost;}set{cost = value;}}}Animal a = new Animal();Console.WriteLine("name={0}, age={1}, cost={2}", a.Name, a.Age, a.Cost);//name=Dolly,age=12,cost=0a.Name = "Joy";//a.Age = 15; error CS0200: Property or indexer 'Animal.Age' cannot be assigned to -- it is read onlya.Cost = 100;Console.WriteLine("name={0}, age={1}, cost={2}", a.Name, a.Age, a.Cost);//name=Joy,age=12,cost=100Dog d = new Dog();Animal a = d;d.Name = "Ani";d.Age = 3;d.Money = 10;d.Cost = 100;Console.WriteLine("{0}-{1}-{2}-{3}", d.Name, d.Age, d.Money, d.Cost);//基类的虚属性可以被使用Console.WriteLine("{0}-{1}-{2}", a.Name, a.Age, a.Cost);a.Cost = 500;Console.WriteLine("{0}-{1}-{2}", a.Name, a.Age, a.Cost);

委托

C#委托(Delegate)类似于 C 或 C++ 中函数的指针,但是类型安全的。
C#委托(Delegate)是存有对某个方法的引用的一种引用类型变量。
C#委托(Delegate)特别用于实现事件和回调方法。
C#委托(Delegate)都派生自 System.Delegate 类。
C#委托可指向一个与其具有相同标签的方法,在运行时可以被替换。
C#委托可以实现多播调用列表。
假如有一个委托的声明如下:
public delegate int MyDelegate (string s);
那么只要定义为int func(string)的函数都可以使用这个委托来调用。这点和C/C++的函数指针完全是一样的,不过C#的委托是由类实现的引用类型变量,具有类型安全机制,可不是简单的函数指针。
C#委托是一个class引用类型变量,因此在实例化时需要使用new来创建内存空间。简单来说,可以将委托的使用看作是委托是特殊的C#类,将委托的作用看作是委托是C++中的函数指针。

委托实例化
委托的实例化有很多种方法,最普通的就是创建委托类型,传递函数对象进去,这里使用new或不使用new操作符都没有太多关系。其次委托可以和匿名函数联合使用,如果你用过C++的Lambda表达式就知道匿名函数的好处啦!

    //声明委托delegate void Del(string str);//函数声明static void Notify(string name){Console.WriteLine("Notification received for: {0}", name);}// 1、创建委托实例,早期C#1.0的用法Del del1 = new Del(Notify);// 2、创建委托实例,现在的用法,不使用new,C#2.0的用法Del del2 = Notify;// 3、使用匿名方法来初始化委托Del del3 = delegate (string name){Console.WriteLine("Notification received for: {0}", name);};// 4、使用lambda表达式实例化委托Del del4 = name => {Console.WriteLine("Notification received for: {0}", name);};

多参数的委托实例化:

    //声明委托delegate int Add(int a, int b);//匿名函数实例化委托,需要在delegate之后重写参数列表Add add = delegate (int a, int b){return a * b;};//Lambda表达式,=号之后直接写参数列表,然后加上=>符号Add add2 = (a, b) =>{return a + b;};Console.WriteLine("10 + 20 = {0}", add(10, 20));Console.WriteLine("10 + 20 = {0}", add2(10, 20));

委托多播
委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。
使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。
使用委托多播可以创建一系列连续的操作,只要一个调用入口即可完成所有的调用操作,而且调用顺序是按照组播的加入顺序依次执行;使用委托多播和事件搭配,可以在一个事件发生后,自动调用组播方法调用列表上的全部方法。这个有点类似QT中的信号槽机制,一个信号发射出去后,与之连接的所有槽函数都会被执行。

    //声明委托delegate int NumberChanger(int n);class DeltegateTest{static int num = 10;//定义static函数public static int AddNum(int p){num += p;return num;}//定义static函数public static int MultNum(int q){num *= q;return num;}}//创建委托实例,分别代理两个静态函数//委托的实例化和类的实例化一样都需要new操作NumberChanger nc1 = new NumberChanger(DeltegateTest.AddNum);NumberChanger nc2 = new NumberChanger(DeltegateTest.MultNum);// 使用委托对象调用方法var v1 = nc1(25);var v2 = nc2(5);Console.WriteLine("V1={0}, V2={1}", v1, v2);//创建多播委托实例NumberChanger nc;//创建调用列表,或者nc=nc1;nc+=nc2;nc = nc1+nc2;//调用多播,会依次调用nc1(5)和nc2(5)var v3 = nc(5);Console.WriteLine("V3={0}", v3);

事件

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
下面的示例是典型的委托和事件最常用的方式:

    //创建一个取水类class FetchWater{//声明一个事件的委托public delegate void FullWaterHandle(string msg);//基于这个委托定义事件模型public event FullWaterHandle FullWaterEvent;//创建取水方法public void BeginFetchWater(){int length = 10;for (int i = 0; i < length; i++){Thread.Sleep(1000);if (i >= 5){//激活事件if (FullWaterEvent != null){FullWaterEvent(msg);}Console.WriteLine("取水完成");return;}else{Console.WriteLine("正在取水,等待时间{0}秒", i + 1);}}}#region 字段private string msg;//创建msg的属性public string Msg{get{return msg;}set{msg = value;}}#endregion}//事件响应类class FullWaterACK{public void OnFullWater(string msg){Console.WriteLine("取水完成,谢谢9527!");}}public static void Main()   { //使用匿名方法创建委托实例FetchWater.FullWaterHandle handle1 = delegate (string msg){Console.WriteLine("{0} : 1 号收到!", msg);};//使用Lambda函数创建委托实例FetchWater.FullWaterHandle handle2 = msg =>{Console.WriteLine("{0} : 2 号收到!", msg);};//创建取水实例FetchWater w = new FetchWater();w.Msg = "9527完成取水";//创建取水完成响应实例FullWaterACK ack = new FullWaterACK(); //将handle1和handle2和取水实例的事件关联起来w.FullWaterEvent += handle1;w.FullWaterEvent += handle2;//将取水响应和取水实例的事件关联起来//也可以直接使用关联:w.FullWaterEvent += ack.OnFullWater;w.FullWaterEvent += new FetchWater.FullWaterHandle(ack.OnFullWater);//调用取水函数,会自动调用相应的委托方法w.BeginFetchWater();Console.ReadKey();}

特性

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签,您可以通过使用特性向程序添加声明性信息。
有三种预定义特性:AttributeUsage,Conditional和Obsolete。

Conditional
这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。如果指定的预处理标识符为真,就执行条件方法中的语句,否则什么也不做。
条件特性标识的函数必须是void,不能返回任何数值。
例如,当调试代码时显示变量的值:

#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{//条件特性关联DEBUG预处理指令[Conditional("DEBUG")]public static void Message(string msg){//只有定义了预处理指令DEBUG时才会执行这句代码Console.WriteLine(msg);}
}
class Test
{static void function1(){Myclass.Message("In Function 1.");function2();}static void function2(){Myclass.Message("In Function 2.");}public static void Main(){Myclass.Message("In Main function.");function1();Console.ReadKey();}
}

这个有点类似C++日志输出时,为了减少在Release模式下的调试代码,通常会这样定义日志输出宏:

#ifdef _DEBUG
//输出日志到控制台
#define LOGOUT(x)  std::cout << x << std::endl;
#else
//空语句,什么也不做
#define LOGOUT(x)
#endif

这里[Conditional]特性按照条件来编译执行方法主体中的代码。

Obsolete
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

    [Obsolete("use NewPrint() instead", false)]public void Print(){Console.WriteLine("这个是老方法!");}public void NewPrint(){Console.WriteLine("这个是新方法!");}

[Obsolete]的第一个参数是描述文字,第二个为false时会在编译时产生编译警告,信息就是提供的描述文字;如果设置为true,在编译时就会出现编译错误。当用新的方法取代旧的方法时,用[Obsolete]可以很容易的在编译时刻就检查出问题。

AttributeUsage
AttributeUsage是用来设计自定义特性的,可以参考这些文章:
AttributeUsage使用说明
AttributeUsage的使用浅析
特性可以给指定的类,方法,接口等增加额外的附加信息,然后在程序中可以获取到这里有用的额外信息。

创建自定义特性(Attribute)
创建并使用自定义特性包含四个步骤:
1、声明自定义特性
2、构建自定义特性
3、在目标程序元素上应用自定义特性
4、通过反射访问特性

下面的代码完成了前三部,后面将在反射一节用代码说明如何通过反射来访问特性:

    // 一个自定义特性 BugFix 被赋给类及其成员[AttributeUsage(AttributeTargets.Class |AttributeTargets.Constructor |AttributeTargets.Field |AttributeTargets.Method |AttributeTargets.Property,AllowMultiple = true)]//构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。//它存储下面的信息://  : bug 的代码编号//  : 辨认该 bug 的开发人员名字//  : 最后一次审查该代码的日期//  : 一个存储了开发人员标记的字符串消息public class DeBugInfo : System.Attribute{private int bugNo;private string developer;private string lastReview;private string message;public DeBugInfo(int bg, string dev, string d){this.bugNo = bg;this.developer = dev;this.lastReview = d;}public int BugNo{get{return bugNo;}}public string Developer{get{return developer;}}public string LastReview{get{return lastReview;}}public string Message{get{return message;}set{message = value;}}}//在目标代码上应用自定义特性[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")][DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]class Rectangle{// 成员变量protected double length;protected double width;public Rectangle(double l, double w){length = l;width = w;}[DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]public double GetArea(){return length * width;}[DeBugInfo(56, "Zara Ali", "19/10/2012")]public void Display(){Console.WriteLine("Length: {0}", length);Console.WriteLine("Width: {0}", width);Console.WriteLine("Area: {0}", GetArea());}}

反射

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优点:
1、反射提高了程序的灵活性和扩展性。
2、降低耦合性,提高自适应能力。
3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:
1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

用途:
1、它允许在运行时查看特性(attribute)信息。
2、它允许审查集合中的各种类型,以及实例化这些类型。
3、它允许延迟绑定的方法和属性(property)。
4、它允许在运行时创建新类型,然后使用这些类型执行一些任务。

接下来继续完成上一节特性的代码,使用反射查询特性信息:

    public static void Main(){Rectangle r = new Rectangle(4.5, 7.5);r.Display();//通过MemberInfo可以查询到所有顶层的Attribute信息System.Reflection.MemberInfo info = typeof(Rectangle);object[] attributes = info.GetCustomAttributes(true);for (int i = 0; i < attributes.Length; i++){//这里会打印Attribute特性的类名:DebugInfoSystem.Console.WriteLine(attributes[i]);}Type type = typeof(Rectangle);//遍历Rectangle类的特性//这里将获取到45,49两个编号的DebugInfo信息foreach (Object attr in type.GetCustomAttributes(false)){//使用as转换类型,如果失败等于nullDeBugInfo dbi = attr as DeBugInfo;if (null != dbi){Console.WriteLine("Bug no: {0}", dbi.BugNo);Console.WriteLine("Developer: {0}", dbi.Developer);Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);Console.WriteLine("Remarks: {0}", dbi.Message);}}//遍历Rectangle类方法的特性//先获取Rectangle类的所有方法foreach (MethodInfo m in type.GetMethods()){//打印查询到的所有类方法Console.WriteLine("{0}", m);//查询该方法中的所有Attribute特性foreach (Attribute a in m.GetCustomAttributes(true)){//使用as转换类型,如果失败等于null//这里一定要判断,会转换失败,不是所有的方法都用DebugInfo附加过信息DeBugInfo dbi = a as DeBugInfo;if (null != dbi){Console.WriteLine("Bug no: {0}, for Method: {1}", dbi.BugNo, m.Name);Console.WriteLine("Developer: {0}", dbi.Developer);Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);Console.WriteLine("Remarks: {0}", dbi.Message);}}}Console.ReadKey();}

下面的代码利用反射抓取类型并调用其方法,全程都只知道类名和方法名:

    //在其他地方定义的类namespace Office{class Printer{public void PrintMessage(string name, string msg){Console.WriteLine("你好,{0}:\n{1}", name, msg);}}}public static void Main(){try{//定义类名称和类方法string className = "Office.Printer";string methodName = "PrintMessage";//获取当前程序集的对象System.Reflection.Assembly asm = Assembly.GetExecutingAssembly();//如果需要反射类库可以使用Assembly.Load的方法//System.Reflection.Assembly ass = Assembly.LoadFrom("xxx.dll");//获取类型信息,记住要判断System.Type t = asm.GetType(className);if (null == t){throw new Exception(String.Format("{0} 类型不支持!", className));}//获取类方法,记住要判断,重要的事情说三遍!!!System.Reflection.MethodInfo mi = t.GetMethod(methodName);if (null == mi){throw new Exception(String.Format("{0}.{1} 类方法不支持!", className, methodName));}//创建对象实例,记住要判断Object obj = System.Activator.CreateInstance(t);if (null == obj){throw new Exception(String.Format("{0} 实例创建失败!", className));}//调用方法,这里使用object[]来传递全部参数mi.Invoke(obj, new object[] { "程序员", "我是反射示例代码" });}catch (Exception e){Console.WriteLine(e);}Console.ReadKey();}

索引

索引器(Indexer) 允许一个对象可以像数组一样被索引访问。当您为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array) 一样。您可以使用数组访问运算符([ ])来访问该类的数据。
索引器的定义有点像属性的定义,有get也有set,同时又像是运算符重载,这个运算符就是下标符号[ ]。简单来说,索引器就是重新定义了下标[ ]操作的功能,下标符号既可以读取,也可以设置,自然索引器就需要提供get和set类似于属性的定义方法。

    //定义元素存储数组和容量private string[] namelist = new string[size];static public int size = 10;public string this[int index]{get{string tmp;if (index >= 0 && index <= size - 1){tmp = namelist[index];}else{tmp = "";}return (tmp);}set{if (index >= 0 && index <= size - 1){namelist[index] = value;}}}IndexedNames names = new IndexedNames();names[0] = "Zara";names[1] = "Riz";names[2] = "Nuha";names[3] = "Asif";

索引器可以被重载,一个类可以定义多个索引器,只要函数的标识不一样就可以。
因此索引器更像是运算符重载,只是形式上看起来像属性而已。
重载多个索引器的一个类:

    //索引器通过[名字&课程编号]查找和保存成绩public int this[string stuName, int courseId]{get{}set{}}//索引器重载,根据[名字]查找所有成绩public List<Scores> this[string stuName]{get{List<Scores> tempList = listScores.FindAll(x => x.StuName == stuName);return tempList;}}//多参数索引器和索引器重载          FindScore fScore = new FindScore();fScore["张三", 1] = 98;fScore["张三", 2] = 100;fScore["张三", 3] = 95;fScore["李四", 1] = 96;//查找所有张三的成绩List<Scores> listScores = fScore["张三"];

泛型

这个很C++中的泛型是同一个意思。C#中可以使用泛型的地方很多,不仅限定于类,方法,接口,还可以在委托,事件中都使用泛型编程。
泛型的主要特征:
1、它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
2、您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
3、您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
4、您可以对泛型类进行约束以访问特定数据类型的方法。
5、泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。

    //定义一个泛型类public class MyGenericArray<T>{private T[] array;public MyGenericArray(int size){array = new T[size + 1];}public T getItem(int index){return array[index];}public void setItem(int index, T value){array[index] = value;}}// 声明一个整型数组MyGenericArray<int> intArray = new MyGenericArray<int>(5);// 声明一个字符数组MyGenericArray<char> charArray = new MyGenericArray<char>(5);

匿名函数与Lambda表达式

使用委托时使用匿名函数和Labmda表达式可以很方便:

    delegate void Print();delegate int Add(int a, int b);static void Main(){//不带参数的Lambda表达式Print p = () =>{Console.WriteLine("Hello");};//带参数的Lambda表达式,下面两种都可以Add fn = (int a, int b) =>//Add fn = (a, b) =>{return a + b;};//不带参数的匿名函数Print p2 = delegate (){Console.WriteLine("Welcom");};//带参数的匿名函数Add fn2 = delegate (int a, int b){return a + b;};Console.WriteLine(fn(3, 4));p();Console.WriteLine(fn(6, 9));p2();Console.Read();}

转载于:https://my.oschina.net/u/3489228/blog/1790653

C#入门03:招牌菜-C#高级功能相关推荐

  1. MVEL快速入门—MVEL流程控制和高级功能(三)

    之前文章 MVEL快速入门-MVEL基础语法讲解(一) MVEL快速入门-MVEL属性和文字讲解(二) 流程控制 实际上MVEL的表达形式不仅仅局限于简单的表达式,他还支持流程控制.使我们能够执行高级 ...

  2. SLAM导航机器人零基础实战系列:(二)ROS入门——10.在实际机器人上运行ROS高级功能预览...

    SLAM导航机器人零基础实战系列:(二)ROS入门--10.在实际机器人上运行ROS高级功能预览 摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了 ...

  3. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序使用高级功能...

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十二篇:为ASP.NET MVC应用程 ...

  4. LearnOpenGL学习笔记—入门03:Hello Triangle

    LearnOpenGL学习笔记-入门03:Hello Triangle 0 前言 1 图形渲染管线 2 顶点输入 3 VAO,VBO 3.1 VAO建立 3.2 VBO建立 4 shader 5 绘制 ...

  5. Excel高级功能 数据工具

    01 数据工具 1.1 数据分列 案例一:要从身份证号码中提取出出生日期 1,增加一列 选中这一列中的所有的身份证号码 结果 案例二:把交易单号分成三列,以-分隔,由于宽度是一样的,也可以使用固定长度 ...

  6. vSphere高级功能(一)——VMotion及SVMotion的实现

    熟悉了vsphere的基本操作,以及新建虚拟机,创建了群集之后,就要开始尝试vsphere的高级功能,首先就是VMotion动态转移机制,可以将虚拟机由一台主机上转移到另外的主机上去,而且中间的中断时 ...

  7. Django by Example·第二章|Enhancing Your Blog with Advanced Features(为博客系统添加高级功能)@笔记

    Django by Example·第二章|Enhancing Your Blog with Advanced Features(为博客系统添加高级功能)@笔记 这本书的结构确实很不错,如果能够坚持看 ...

  8. 同城外卖APP开发高级功能详情

    同城外卖APP开发,同城外卖APP开发高级功能.因为社会经济发展飞速发展趋势,大家生活水准也在不断提升,很多人因为生活节奏感较快,没有时间自身去烹制饮食搭配,点外卖就变成了生活中的常态化,而这一切生活 ...

  9. 出海复盘周报:出海网赚、高级功能、海外获客

    (全文3043字,阅读时间约6~7分钟) 我现在做的是出海产品,所以打算出一个系列文章的新坑,名字叫<出海复盘周报>--是将我过去一周的产品工作进行复盘和总结,以「主题关键词」的形式进行叙 ...

最新文章

  1. ulipad 编辑器下载
  2. openssh无法登录:server responded algorithm negotiation failed”
  3. 95% CI, 置信区间 Confidence Interval
  4. idea 无法打开项目_Premiere出现quot;项目看来已经损坏,无法打开”的解决方法...
  5. 从操作系统层面描述线程的五种状态
  6. php cdi_配置CDI对话的超时
  7. ubuntu下NDK环境配置
  8. 不小心执行了 rm -rf,除了跑路还有其他办法吗?
  9. ios把数据传递到另一个页面_iOS 应用之间的跳转和数据传递
  10. java锁包读写锁_Java并发包7--读写锁ReentrantReadWriteLock的实现原理解析
  11. Karoly Nyisztor
  12. 非线性鲁棒控制器_Sliding Mode_滑模控制
  13. 重启计算机之前无法刷新,更新完补丁不断提示是否重启电脑的解决方法
  14. 百度API---详解
  15. Android 常用RGB值以及中英文名称
  16. javaMail邮件发送功能(多收件人,多抄送人,多密送人,多附件)
  17. 操作系统 - Linux - Ubuntu
  18. 2种js动态绑定事件方法
  19. linux cad 安装教程,如何在Ubuntu 18.04/16.04/Linux中安装FreeCAD 0.18
  20. python 处理excel文件,按某一列值生成多个excel文件

热门文章

  1. 营销就是不流血的战争
  2. 在Mac OS X 10.8中配置Apache + PHP + MySQL
  3. Phrase-BERT: Improved Phrase Embeddings from BERT with an Application to Corpus Exploration论文阅读笔记
  4. 超全伏特加免抠摄影素材网站整理
  5. centos7 安装 apollo
  6. Proxmark​​​​​​​3介绍
  7. python60行绘图程序_天底下最简单的QT画图板,就一个类,60行代码
  8. webpack系统学习(六)打包分析,Preload和Prefetch
  9. 百度飞桨《青春有你2》Python小白逆袭大神活动总结与感悟
  10. 不懂任何VBA代码,也可实现数据透视表自动更新