黑马程序员C#学习笔记
错误的理解希望能够得到大家的指正,谢谢!
/*实现了IDisposable接口的所有的对象在使用完以后要进行Dispose()资源的释放,可以使用using(){}进行资源管理*/
//创建连接是非常耗时的,因此不要每次操作都创建连接。SQL语句中的关键字应该大写。
//1个数据库能够承载的连接是有限的,所以SqlConnection在程序中不能一直保持Open。
//对于数据库来说,连接是最宝贵的资源,用完了以后一定要Close、Dispose。
// 类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。
//元数据:用于描述要素、数据集或数据集系列的内容、覆盖范围、质量、管理方式、数据的所有者、数据的提供方式等有关的信息。元数据最本质、最抽象的定义为:data about data(关于数据的数据)。它是一种广泛存在的现象,在许多领域有其具体的定义和应用。
//元数据在软件构造领域定义:在程序中不是被加工的对象,而是通过其值的改变来改变程序的行为的数据。它在运行过程中起着以解释方式控制程序行为的作用。在程序的不同位置配置不同值的元数据,就可以得到与原来等价的程序行为。
.NET Framework基础类型:
.NET所有类型都继承自System.Object。C#类型体系包含两种类型:值类型,引用类型。
值类型继承自System.ValueType。而System.ValueType继承自System.Object。
指针:在信息工程中指针是一个用来指示一个内存地址的计算机语言的变量或中央处理器(CPU)中寄存器(Register)。
指针一般出现在比较近机器语言的语言,如汇编语言或C语言。在使用一个指针时,一个程序既可以直接使用这个指针所存储的内存地址,又可以使用这个地址里存储的变量或函数的值。
寄存器:寄存器是中央处理器内的组成部分。寄存器是有限存储容量的高速存储部件,它们可用来暂存指令、数据和位址。寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。
C#中使用指针:为了保持类型安全,默认情况下C#不支持指针运算。不过可以通过使用unsafe关键字来定义允许使用指针的不安全上下文。
C#中的不安全代码不一定是危险的,只是其安全性无法由CLR进行验证。编译不安全代码时需要使用csc /unsafe *.cs。
class Program { unsafe static void Method(int* parameter)//声明一个int类型的指针 { *parameter += *parameter;//指针相加 } unsafe static void Main() { int parameter = Convert.ToInt32(Console.ReadLine()); Method(¶meter); Console.WriteLine(parameter); Console.ReadKey(true); } }
值类型:值类型直接存储值
一种由类型的实际值表示的数据类型。如果向一个变量分配值类型,则该变量将被赋以全新的值副本。
C#中的值类型包括结构类型和枚举类型两大类以及byte、int、long、float、double、char、boolean。
引用类型:引用类型存储的是对值的引用
由类型的实际值引用表示的数据类型。如果为某个变量分配一个引用类型,则该变量将引用(或"指向")原始值,不会创建副本。
引用类型包括类、接口、委托和装箱值类型。C#有两个内置的引用类型:object类型和string类型。
常量与变量:
常量:常量又叫常数,主要用来存储在程序运行的过程中值不会改变的数据。常量被声明为字段,通过const关键字声明,常量必须在声明时赋值。
变量:变量是指在程序运行的过程中值可以改变的数据。
数据类型转换:
显示类型转换:显示类型转换是将高精度数值转换为低精度数值,必须指明将要转换的目标类型。由于数据类型的差异,有可能丢失部分数据。
隐式(自动)类型转换:隐式类型转换又称自动类型转换,是将低精度数值转换为高精度数值,可以直接赋值而不用指明将要转换的目标类型。
进制转换:
int i = 10; Console.WriteLine("十进制转二进制:"+Convert.ToString(i, 2)); Console.WriteLine("十进制转八进制:"+Convert.ToString(i, 8)); Console.WriteLine("十进制转十六进制:"+Convert.ToString(i, 16)); Console.WriteLine("二进制转十进制:"+Convert.ToInt32("1010",2)); Console.WriteLine("八进制转十进制:"+Convert.ToInt32("10",8)); Console.WriteLine("十六进制转十进制:" + Convert.ToInt32("a",16)); Console.ReadKey(true);
C#中运算符的使用:
算术运算符:+、-、*、/、%
Console.WriteLine(10 +0.5+.5);//"+"运算符用于两个数值相加,当其中一个或两个操作数都是字符或字符串时,表示相连 Console.WriteLine("10" + 5); Console.WriteLine('a' + "10"); int i = -10; Console.WriteLine(-i);//"-"运算符可以是一元运算符也可以是二元运算符 Console.WriteLine(10 - 0.5 - .5);//一元运算符表示只用一个参数的运算,二元运算符表示符号左右两边用到了两个参数 Console.WriteLine(10 * i);//"*"运算符用于计算操作数的积 Console.WriteLine((double)6 / 25);//"/"运算符用于计算操作数的商 Console.WriteLine(100 % 6);//"%"运算符用于计算操作数的余数 Console.ReadKey(true);
赋值运算符:=、+=、-=、*=、/=、%=、??
int i = 100;//"="运算符表示将右边的值赋给左边的变量或常量 i += 10;//"+="运算符等同于 i=i+10 i -= 10;//"-="运算符等同于 i=i-10 i /= 10;//"/="运算符等同于 i=i/10 i *= 10;//"*="运算符等同于 i=i*10 i %= 3;//"%="运算符等同于 i=i%10 Console.WriteLine(i); int? x = null;//int? 可空类型,表示一个可能是空,也可能是int的结构 int y = x ?? 10;//如果x是null,则y=10,否则y=x Console.WriteLine(y); Console.ReadKey(true);
关系运算符:==、!=、>、<、>=、<=、
关系运算符用于实现对两个值的比较运算,关系运算符在完成对两个操作数的比较运算后会返回一个代表运算结果的布尔值。
bool result = 2 == 1; Console.WriteLine(result); Console.WriteLine(2 != 3); Console.WriteLine(5 > 6); Console.WriteLine(6>=5); Console.ReadKey(true);
逻辑运算符:&&、||、!
Console.WriteLine(true||false);//逻辑或运算,运算符左右有一个true则结果为true,否则为false Console.WriteLine(true && false);//逻辑与运算,运算符左右有一个false则结果为false,否则为true Console.WriteLine(!true);//逻辑非运算,对布尔值取反 Console.ReadKey(true);
条件运算符:?:
static void Main(string[] args) { Console.Write("用户名:"); string userName = Console.ReadLine(); Console.Write("密码:"); string password = Console.ReadLine(); bool result = (userName == "admin" && password == "admin"); string strInfo = result ? "登录成功!" : "登录失败!";//result为true时就把"登录成功!"赋给strInfo Console.WriteLine(strInfo); Console.ReadKey(true); }
移位运算符:<<、>>
左移位运算:按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。
在数字没有溢出的前提下,对于正数和负数,左移n位就相当于乘以2的n次方。
右移位运算:按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位的空位补符号位,即正数补零,负数补1。
对于正数和负数,右移n位就相当于除以2的n次方。
class Program { static void Main(string[] args) { //1111111111111111111111111111111111111111111111111111111110011100 << 3 //=1111111111111111111111111111111111111111111111111111110011100000 (-100 << 3 = -800) //0000 1100 1000 >> 3=0000 0001 1001 (200 >> 3 = 25) int one = -100, two = 200; int temp = one << 3;//将变量one的值左移3位。 Console.WriteLine("-100 << 3 = " + temp.ToString()); temp = two >> 3;//将变量two的值右移3位。 Console.WriteLine("200 >> 3 = " + temp.ToString()); Console.ReadKey(true); } }
is 运算符:
is运算符用于检查变量是否为指定的类型,如果是返回true,否则返回false。
class Program { static void Main(string[] args) { int i = 10; bool result = i is int;//判断变量 i 是否为整型 Console.WriteLine(result.ToString()); Console.ReadKey(true); } }
枚举类型:
class Program { enum 功夫 //使用enum创建枚举 { 降龙十八掌 = 0, 如来神掌 = 1, 葵花宝典 = 2, 九阳神功 = 3, 独孤九剑 = 4, 太极拳 = 5 } static void Main(string[] args) { Console.WriteLine("小子,我看你骨骼惊奇,必是练武奇才,将来维护宇宙正义与和平的重任就交给你了!"); int 学习 = 0; if (学习 > 5 || int.TryParse(Console.ReadLine(),out 学习)==false) { Console.WriteLine("你什么也没有学会!"); } else { switch (学习) { case (int)功夫.降龙十八掌: Console.WriteLine("你学会了降龙十八掌!"); break; case (int)功夫.如来神掌: Console.WriteLine("你学会了如来神掌!"); break; case (int)功夫.葵花宝典: Console.WriteLine("你学会了葵花宝典!"); break; case (int)功夫.九阳神功: Console.WriteLine("你学会了九阳神功!"); break; case (int)功夫.独孤九剑: Console.WriteLine("你学会了独孤九剑!"); break; case (int)功夫.太极拳: Console.WriteLine("你学会了太极拳!"); break; default: Console.WriteLine("你什么也没有学会!"); break; } } Console.ReadKey(true); } }
Char类的使用:
class Program { static void Main(string[] args) { char a = 'a', b = '8', c = 'L', d = '.', e = ' ', f = ' '; Console.WriteLine("IsLetter方法判断a是否为字母:{0}", Char.IsLetter(a)); Console.WriteLine("IsDigit方法判断b是否为数字:{0}", Char.IsDigit(b)); Console.WriteLine("IsLetterOrDigit方法判断c是否为字母或数字:{0}", Char.IsLetterOrDigit(c)); Console.WriteLine("IsLower方法判断a是否为小写字母:{0}", Char.IsLower(a)); Console.WriteLine("IsUpper方法判断c是否为大写字母:{0}", Char.IsUpper(c)); Console.WriteLine("IsPunctuation方法判断d是否为标点符号:{0}", Char.IsPunctuation(d)); Console.WriteLine("IsSeparator方法判断e是否为分隔符:{0}", Char.IsSeparator(e)); Console.WriteLine("IsWhiteSpace方法判断f是否为空白:{0}", Char.IsWhiteSpace(f)); Console.ReadKey(true); } }
流程控制语句:
选择结构:
if(boolean ) {语句块} else{语句块} switch(表达式) {case 常量表达式: 语句块 break; default:语句块 break;}
循环结构:
while(boolean){语句块} 、do{语句块}while(boolean) for(声明int类型变量并赋值;判断产生循环的条件;要循环的表达式){语句块} foreach(类型 循环变量名 in 集合){语句块}
跳转语句:
break(终止循环)语句只能应用在switch、while、do...while、for或foreach语句中,break语句应用于循环作用域中。 continue(忽略本次循环,继续下次循环)语句只能应用于while、do...while、for或foreach语句中,continue语句应用于循环作用域中。 goto语句用于将控制转移到由标签标记的语句。goto 标签; 标签: 语句块 return语句用于终止当前执行的方法、可以有返回值。
数组:
一维数组:
int[] arr;/*声明数组*/ /*初始化数组*/ int[] arr = new int[5];/*表示数组长度是5*/ int[] arr = { 1, 2, 3 }; int[] arr = new int[] { 1, 2, 3};
二维数组:
int[,] arr;/*声明数组*/ /*初始化数组*/ int[,] arr1 = new int[3, 3]; int[,] arr2 = new int[2,2] {{1,2},{3,4}}; static void Main() { int[,] arr = new int[,] { { 1, 3 }, { 2, 4 }, { 5, 7 }, { 6, 8 } }; Console.Write("数组的行数:" + arr.GetLength(0));//输出二维数组的行数 Console.Write("\t数组的列数:" + arr.GetLength(1));//输出二维数组的列数 Console.WriteLine(); Console.WriteLine("循环输出数组中的元素:"); for (int y = 0; y < arr.GetLength(1);y++ ) { Console.Write("\t第" + y + "列"); } Console.WriteLine(); for (int i = 0; i < arr.GetLength(0);i++ )//遍历二维数组 { string str = ""; for (int x = 0; x < arr.GetLength(1);x++ ) { str = str + Convert.ToString(arr[i, x])+"\t"; } Console.WriteLine("第"+i+"行: "+str); } Console.ReadKey(true); }
ArrayList:动态数组,可以动态的添加和删除元素。ArrayList的容量可以根据需要自动扩充,只能是一维的形式。
class Program { static void Main() { int[] arr = new int[] { 1, 2, 3, 4, 5, 6 }; Array.Reverse(arr);//反转整个一维数组中元素的顺序 foreach (int i in arr) { Console.Write(i); } Console.WriteLine(); ArrayList list = new ArrayList(arr);//实例化ArrayList动态数组并赋初值 list.Add("你好!");//增加数据到动态数组中 list.AddRange(arr);//添加集合到动态数组的末尾 Console.WriteLine(list.Contains(5));//判断指定元素是否在数组中 Console.WriteLine(list.IndexOf(5));//输出指定元素在数组中的索引位置,如果没有找到输出-1 list.Insert(0, "Hi!");//插入数据到指定的位置 list.Remove("你好!");//"你好!"被看做是一个元素,根据元素内容移除一个指定的元素 list.RemoveAt(1);//根据元素的索引移除一个指定的元素 list.RemoveRange(1, list.Count - 1);//移除指定范围内的所有元素 foreach (object obj in list) { Console.Write(obj); } list.Clear();//移除所有元素 Console.ReadKey(true); } }
哈希表Hashtable:散列表也叫哈希表。它表示键/值(key,value)对的集合。
class Program { static void Main() { Hashtable htable = new Hashtable();//实例化Hashtable对象 htable.Add(1, "你好!");//向哈希表中添加元素 htable.Add(2, "很好!"); for (int i = 0; i <= htable.Count;i++ ) { Console.Write(htable[i]); } Console.WriteLine(); htable.Remove(1);//移除元素的键 Console.WriteLine(htable[2]);//输出哈希表中指定键的值 htable.Clear(); htable.Add("姓名", "张耕明"); htable.Add("年龄", 26); Console.WriteLine("\t键\t值"); foreach (DictionaryEntry dicEntry in htable) { Console.WriteLine("\t" + dicEntry.Key + "\t" + dicEntry.Value); } Console.WriteLine(htable.ContainsKey("姓名"));//判断哈希表中是否包含指定的键 Console.WriteLine(htable.ContainsValue("张耕明"));//判断哈希表中是否包含指定的值 Console.ReadKey(true); } }
静态和非静态:
static class 静态类//静态类不能实例化,只能包含静态成员和静态方法,静态成员或方法只能由类来访问 { private static string _str; static 静态类()//静态构造函数不能带有参数,不能有访问修饰符,在调用类成员时执行 { _str = "Static Class!"; } public static void StaticDemo() { Console.WriteLine(_str); } } class 非静态类 { private string _str;//非静态字段只能由对象来访问
public 非静态类(string source) { this._str = source;//this等同于类的实例对象:“非静态类 c = new 非静态类();” } public void Demo() { Console.WriteLine("非静态方法!---" + this._str); }
} 静态类.StaticDemo(); 非静态类 noStaticDemo = new 非静态类("非静态构造函数!"); noStaticDemo.Demo();
特性:特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性。
class Program { static void Main(string[] args) { MemberInfo info = typeof(Demo);//提供对成员元数据的访问
//Attribute.GetCustomAttribute(类的元数据, 要搜索的自定义属性的类型或者基类型)
特性 attribute = (特性)Attribute.GetCustomAttribute(info, typeof(特性));
attribute.Show(); Console.ReadKey(true); } } [AttributeUsageAttribute(AttributeTargets.All, AllowMultiple = true, Inherited = false)]//可应用任何元素、允许应用多次、不继承到派生类 class 特性 : System.Attribute { private string _name; public string Name { get { return _name; } } private int _age; public int Age { get { return _age; } } public 特性(string name, int age) { this._name = name; this._age = age; } public void Show() { Console.WriteLine("姓名:{0},年龄:{1}", _name, _age); } } [特性("张耕明", 28)] public class Demo { }
序列化和反序列化:序列化就是指将对象状态转换为可存储或可传输格式的过程,而反序列化则是从物理介质或流上获取数据,并还原为对象的过程。
[Serializable]//指示一个类可以序列化 public class 序列化和反序列化 { public string Name { get; set; } public int Age { get; set; } public 序列化和反序列化(string name, int age) { this.Name = name; this.Age = age; } public static void BinarySerialize(序列化和反序列化 source) { FileStream fs = new FileStream("MySerialize.bin", FileMode.Create); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(fs, source);//执行二进制序列化 fs.Dispose(); } public static 序列化和反序列化 BinaryDeserialize() { FileStream fs = new FileStream("MySerialize.bin", FileMode.Open, FileAccess.Read, FileShare.Read); BinaryFormatter formatter = new BinaryFormatter(); 序列化和反序列化 serialize = formatter.Deserialize(fs) as 序列化和反序列化;//执行二进制反序列化 fs.Dispose(); return serialize; } } 序列化和反序列化.BinarySerialize(new 序列化和反序列化("张耕明", 28));//序列化 序列化和反序列化 deserialize = 序列化和反序列化.BinaryDeserialize();//反序列化 Console.WriteLine("姓名:{0},年龄:{1}", deserialize.Name, deserialize.Age); 序列化和反序列化 serialize = new 序列化和反序列化("张耕明", 29); XmlSerializer xmlSerialize = new XmlSerializer(typeof(序列化和反序列化)); StreamWriter streamW = new StreamWriter("Myserialize.xml"); xmlSerialize.Serialize(streamW, serialize);//执行xml序列化 streamW.Dispose(); FileStream fileStream = new FileStream("Myserialize.xml", FileMode.Open); 序列化和反序列化 xmlDeserialize = xmlSerialize.Deserialize(fileStream) as 序列化和反序列化;//执行xml反序列化 Console.WriteLine("XML反序列化---姓名:{0},年龄:{1}", xmlDeserialize.Name, xmlDeserialize.Age); fileStream.Dispose();
属性和方法:
属性是实体的描述性性质或特征,具有数据类型、域、默认值三种性质。属性有访问器,当读取属性时执行get访问器的代码块;当向属性分配一个新值时执行set访问器的代码块。不具有set访问器的属性被视为只读属性,不具有get访问器的属性被视为只写属性,同时具有get和set访问器的属性是可读可写属性。属性不能作为ref参数或者out参数传递。
访问级别:
public:公共的、全局的,表示不限制对该类成员的访问
private:私有的,表示只有该类本身可以访问
protected:受保护的,表示只有该类本身和他的子类可以访问
internal:内部的,表示程序集内可访问
构造函数(方法)和析构函数:
构造函数:构造函数是在创建给定类型的对象时执行的类方法。构造函数具有与类相同的名称,它通常用来初始化新对象的数据成员。
析构函数:是以~加类名来命名的,.NET Framework类库有垃圾回收功能。当某个类的实例被认为是不再有效,并符合析构条件时,.NET Framework类库的垃圾回收功能就会调用该类的析构函数实现垃圾回收。一个类中只能有一个析构函数,并且无法调用析构函数,它是被自动调用的。
class Program { string id, name; ~Program()//析构函数 { Console.WriteLine("垃圾回收!"); Console.ReadLine(); } public Program() { id ="01"; name = "未命名"; } public Program(string id, string name) { this.id = id;
this.name = name; } static void Main() { Program p = new Program();//使用没有参数的构造函数 Program p1 = new Program("001", "张耕明");//使用有参数的构造函数 Console.WriteLine(p.name); Console.WriteLine(p1.name); Console.ReadKey(true); } }
方法的重载:方法的重载要求参数的类型不同或者参数的个数不同,与方法的返回值无关。
class Program { static void Main(string[] args) { Program.说("Computer"); Program.说("联想", 5); Console.ReadKey(true); } static void 说(string 姓名) { Console.WriteLine("电脑说:我的名字是{0}。" ,姓名); } static void 说(string 品牌,int 年龄 ) { Console.WriteLine("电脑说:我诞生于{0}公司,现在{1}岁了!", 品牌, 年龄); } }
结构:结构是一种值类型,通常用来封装一组相关的变量。C#中使用struct关键字来声明结构,struct不能从class继承,也不能作为class的基类。
支持接口继承。
class Program { public struct Rectangle//定义一个矩形结构 { private double _width; private double _height; /// <summary> /// 构造函数,初始化矩形的宽和高 /// </summary> /// <param name="x">矩形的宽</param> /// <param name="y">矩形的高</param> public Rectangle(double x, double y) { _width = x; _height = y; } /// <summary> /// 计算矩形的面积 /// </summary> /// <returns>矩形面积</returns> public double Area() { return _width * _height; } } static void Main() { Rectangle rectangle = new Rectangle(3.5, 10);//使用构造函数实例化矩形结构并赋初值 Console.WriteLine("矩形的面积是:" + rectangle.Area()); Console.ReadKey(true); } }
深拷贝和浅拷贝:
class 深拷贝和浅拷贝 { private string _shallow; /// <summary> /// 浅拷贝 /// </summary> public void ShallowCopy() { 深拷贝和浅拷贝 instance1 = new 深拷贝和浅拷贝(); 深拷贝和浅拷贝 instance2 = instance1;//浅拷贝,拷贝对象和源对象都指向同一个实例 instance1._shallow = "Hello C#!"; instance2._shallow = "Shallow Copy!"; Console.WriteLine("浅拷贝:{0}、{1}", instance1._shallow, instance2._shallow); } /// <summary> /// 深拷贝 /// </summary> public void DeepCopy() { int a = 100; int b = a;//深拷贝,拷贝对象和源对象相互独立,不共享任何实例数据 a = 500; b = 1000; Console.WriteLine("深拷贝:{0}、{1}", a, b); } }
装箱与拆箱:
class 装箱与拆箱 { public void Demo()//装箱只发生在值类型,引用类型本身就已经装在箱子里了(箱子指托管堆) { int i = 0; object obj = i;//装箱就是在托管堆中创建值类型的实例,然后返回一个新对象的地址 i = (int)obj;//拆箱就是获取箱子中原本属于值类型的指针 Hashtable hashTable = new Hashtable();//大量的装箱与拆箱会造成多余的对象,会影响系统性能。 for (int x = 0; x < 10; x++) { hashTable.Add(x, obj); } int y = 10;//值类型使用时避免隐式的和引用类型转换,尽可能以显示的方式来实现 object o = y.ToString();//ToString方法由int类型重写,因此不会装箱 } List<int> 泛型集合 = new List<int>();//在操作值类型时尽量使用泛型集合来代替非泛型集合,会得到性能上的提升 泛型集合.Add(1);//不会发生装箱
使用using:
using System;//引入命名空间 using T = System.Threading;//使用using创建命名空间别名 class Demo { public Demo() { T.Thread.Sleep(1000); } } using (SqlConnection sqlConn = new SqlConnection()) { }//强制资源清理
面向对象:面向对象程序设计可以被视作一种在程序中包含各种独立而又互相调用的单位和对象的思想。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象。因此它们都可以被看作是负有责任的角色。面向对象的编程方式具有封装、继承和多态性等特点。
类:类是对象这个概念在面向对象编程语言中的反映,是相同对象的集合。类描述了在概念上有相同含义的对象,并为这些对象统一定义了属性和方法。
类与对象的区别:类是具有相同或相似结构、操作和约束规则的对象组成的集合,而对象是某一类的具体化实例,每一个类都是具有某些共同特征的对象的抽象。
public class Person //类使用class关键字来声明,public是类的修饰符 { public string name; public int age; } Person p = new Person() { name = "张耕明", age = 28 };//类的一个对象
修饰符:
public---不限制对该类的访问
abstract---抽象类,不允许建立类的实例
sealed---密封类,不允许被继承
封装:隐藏内部实现,内部的更改不会影响到外部原有的实现。让外部接口倾向于稳定,把变化的和不变化的分开。
class Demo { public void Method()//提供与外部交互的接口 { Show(); } private void Show()//隐藏的内部实现 { Console.WriteLine("Hello World!");//内部的更改不会影响到外部原有的实现 } }
继承:通过继承可以创建子类(派生类)和父类之间的层次关系,子类(派生类)可以从其父类中继承属性和方法,通过这种关系模型可以复用现有的代码,减少代码的重复。C#中一次只允许继承一个类,不能同时继承多个类。
class Car { public string color = "红色"; } class Bicycle : Car //使用冒号加上类名来表示继承关系 { public Bicycle() { Console.WriteLine(this.color);//通过继承得到的基类成员 } }
多态:多态性是允许你将父对象设置成为和一个或更多的它的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。就是说不同的类在进行同一种操作时可以有不同的行为,这是一种改写对象行为的技术。
class Program { static void Main(string[] args) { Person[] person = new Person[] { new GoodPerson(), new BadPerson() };//person数组中不同的两个类 foreach (Person p in person) { p.Say();//输出了不同的行为 } Console.ReadKey(true); } } abstract class Person { public abstract void Say(); } class GoodPerson : Person { public override void Say() { Console.WriteLine("好男孩:放开那个女孩!"); } } class BadPerson : Person { public override void Say() { Console.WriteLine("坏男孩:我投降!"); } }
接口:接口是一种用来定义程序的协议,它描述可属于任何类或结构的相关行为。接口不能包含字段。
类和结构可以像类继承基类或者结构一样从接口继承,但是可以继承多个接口。当类或结构继承接口后,必须要去实现接口。
接口的特征:
继承接口的任何非抽象类型都必须实现接口的所有成员;
不能直接实例化接口;
接口可以包含事件、索引器、方法和属性;
接口不能包含方法的实现;
类或者结构可以从多个接口继承,接口自身也可以从多个接口继承。
class Program { static void Main(string[] args) { IMyInterface demo = new Demo();//使用派生类对象实例化接口 demo.Method(); Console.ReadKey(true); } } interface IMyInterface //定义接口 { void Method(); } class Demo : IMyInterface { public void Method() { Console.WriteLine("实现接口!"); } }
显示的实现接口成员:如果类实现两个接口,并且这两个接口具有相同签名的成员,那么在类中实现该成员将导致两个接口都使用该成员作为它们的实现,这时可以显示的实现接口成员。
显示接口成员实现中不能包含访问修饰符、abstract、virtual、override或static修饰符。
显示接口成员是属于接口的成员,而不是类的成员,只能使用接口对象来访问,不能使用类对象来访问。
class Program { static void Main(string[] args) { IOne one = new Demo();//使用派生类对象实例化接口 ITwo two = new Demo(); one.Show(); two.Show(); Console.ReadKey(true); } } interface IOne { void Show(); } interface ITwo { void Show(); } class Demo : IOne, ITwo//继承多个接口 { void IOne.Show()//显示接口成员实现 { Console.WriteLine("实现接口一!"); } void ITwo.Show() { Console.WriteLine("实现接口二!"); } }
抽象类与抽象方法:抽象类主要用来提供多个派生类可共享的基类的公共定义。
它与非抽象类的主要区别:
抽象类不能直接实例化。
抽象类中可以包含抽象成员,但非抽象类中不可以包含抽象成员。
抽象类不能被密封。
abstract class Person //声明抽象类。派生类只能继承一个抽象类,可以继承任意多个接口。 { public abstract void Say();//抽象方法没有实现部分,必须在派生类中重写,只能在抽象类中出现。 public virtual void Money()//虚方法必须有实现部分,可以不用在派生类中重写,可以在非抽象类中出现。 { Console.WriteLine("我挣钱了!"); } } class Programmer : Person { public override void Say() { base.Money(); Console.WriteLine("我来自北京中关村黑马训练营!"); } } class Program { static void Main() { Programmer programmer = new Programmer(); programmer.Say(); Console.ReadKey(true); } }
密封类与密封方法:密封类可以用来限制扩展性,如果密封了某个类,则其它类不能从该类继承。如果密封了某个成员,则派生类不能重写该成员的实现。一般情况下不应该密封类和成员。并不是每个方法都可以声明为密封方法,密封方法只能用于对基类的虚方法或者抽象方法进行重写。
满足如下条件,则应该将其密封:
类是静态类。
类包含带有安全敏感信息的继承的受保护成员。
类继承多个虚成员,并且密封每个成员的开发和测试的开销明显大于密封整个类。
类是一个要求使用反射进行快速搜索的属性,密封属性可以提高反射在检索属性时的性能。
abstract class Person { public abstract void Say(); } sealed class Man : Person //声明一个密封类,该类继承自另一个类。 { public sealed override void Say()//密封方法只能用于对基类的虚方法或者抽象方法进行重写 { Console.WriteLine("这是一个密封类和密封方法的演示!"); } }
泛型:泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个类型的占位符。
泛型集合类可以将类型参数用作它所存储的对象的类型的占位符;类型参数作为其字段的类型及其方法的参数类型出现。泛型方法可以将其类型参数用作其返回值的类型或者其某个形参的类型。
使用泛型类型的优点:
泛型类和泛型方法同时具备可重用性、类型安全和效率,泛型通常用与集合以及作用于集合的方法一起使用。
可以创建自定义泛型类型和方法,以提供自己的通用解决方案,设计类型安全的高效模式。
可以对泛型类进行约束以访问特定数据类型的方法。
关于泛型数据类型中使用的类型的信息可以在运行时通过反射获取。
//泛型的参数 T 可以看作是一个占位符,它不是一种类型,它仅代表了某种可能的类型。 public class WideClass<T> //创建一个泛型类 { public T Info { get; set; } public void Method() { Console.WriteLine(Info); } } class Program { static void Main(string[] args) { WideClass<string> instance = new WideClass<string>(); instance.Info = "泛型演示!"; instance.Method(); Console.ReadKey(true); } }
枚举:枚举(enum)是值类型的一种特殊形式,枚举类型有名称、基础类型和一组字段。基础类型必须是一个有符号(或无符号)整数类型,如果没有显示的声明基础类型,则使用Int32。字段是静态文本字段,其中的每一个字段都表示常数。
程序中使用枚举的限制:不能定义自己的方法;不能实现接口;不能定义属性和事件。
位域支持的运算符:
1、“|”表示两边求并集(元素相加,相同元素只出现一次)。
2、“&”表示两边是否其中一个是另外一个的子集,如果是返回子集,否则返回0。
3、“^”表示从两者的并集中去除两者的交集。
4、“&(~ )”从组合状态中去掉一个元素。
/// <summary> /// 枚举类型可以使用Flags位域标志,表示这些枚举类型可以作为位域(即一组标志)处理。 /// C#位域主要用于.Net里面对于某一个事物有多种混合状态时使用,为了更好的实现混合状态,我们可以在枚举中加入位域标签。 /// </summary> [Flags] public enum StudyTime { //定义枚举常量时使用2倍递增,这样可以使组合的枚举常量中的各个标志都不重叠。 星期一 = 1 << 0, 星期二 = 1 << 1, 星期三 = 1 << 2, 星期四 = 1 << 3, 星期五 = 1 << 4, 所有时间 = 星期一 | 星期二 | 星期三 | 星期四 | 星期五 } public enum Course { 计算机 = 1 << 0, 英语 = 1 << 1, 国学 = 1 << 2, 数学 = 1 << 3, } class Program { static void Main(string[] args) { //按位或"|"、按位与"&"、按位异或"^"、按位取反"~"。 Console.WriteLine(1 | 2);//将十进制转换为二进制,然后求并集。 Console.WriteLine(1 & 3);//将十进制转换为二进制,然后求交集。 Console.WriteLine(1 ^ 3);//将十进制转换为二进制,然后从并集中去除交集。 Console.WriteLine(~10);//(~0=-1,~1=-2,~2=-3,~-1=0,~-2=1,~-3=2)运算规则:加1后取负 Console.WriteLine("\t课程表"); Dictionary<Course, StudyTime> dictionary = new Dictionary<Course, StudyTime>(); StudyTime[] dateArr = new StudyTime[] { StudyTime.星期一, StudyTime.星期二, StudyTime.星期三, StudyTime.星期四, StudyTime.星期五 }; dictionary.Add(Course.计算机, StudyTime.所有时间); dictionary.Add(Course.国学, StudyTime.星期一 | StudyTime.星期二); dictionary.Add(Course.数学, StudyTime.星期三 | StudyTime.星期四); dictionary.Add(Course.英语, StudyTime.星期三|StudyTime.星期四|StudyTime.星期五); foreach (StudyTime date in dateArr) { Console.Write(date+":"); foreach (var item in dictionary) { if ((item.Value & date) > 0) { Console.Write(item.Key+" "); } } Console.WriteLine(); } StudyTime studyTime = StudyTime.星期一 | StudyTime.星期二|StudyTime.星期三; Console.WriteLine("使用 | 符号(求并集): " + studyTime.ToString()); studyTime = (StudyTime.星期二 | StudyTime.星期三) & studyTime; Console.WriteLine("使用 & 符号(求交集):" + studyTime.ToString()); studyTime = (StudyTime.星期二|StudyTime.星期三|StudyTime.星期五) ^ studyTime; Console.WriteLine("使用 ^ 符号(从并集中去除交集):" + studyTime.ToString()); Console.WriteLine("使用&(~)从组合状态中去掉一个元素:"+(StudyTime.所有时间 & (~studyTime)).ToString());
studyTime = ~(StudyTime.星期一 | StudyTime.星期二); Console.WriteLine("使用 ~ 符号(加1后取负):" + studyTime.ToString()); Console.ReadKey(true); } }
委托:委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任何可以声明类的地方都可以声明委托。
定义委托,就是定义可以代表方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
class Program { static void Main(string[] args) { Program p = new Program(); SayDelegate demoDelegate = delegate(string content) { Console.WriteLine(content); }; demoDelegate("使用委托调用匿名方法的演示!"); SayDelegate chinaDelegate=p.SayChinese;//委托是一个可以代表方法的类型 SayDelegate englishDelegate=p.SayEnglish; p.Say("大家好,我叫张耕明!", chinaDelegate);//委托使方法可以当作另一个方法的参数进行传递 p.Say("Hello,My name is ZGM", englishDelegate); SayDelegate moreDelegate = p.SayChinese; moreDelegate += p.SayEnglish;//多个方法绑定到同一个委托,将依次调用所绑定的方法 moreDelegate("多个方法绑定到委托的演示!"); moreDelegate -= p.SayEnglish;//取消绑定的方法 moreDelegate("取消绑定方法的演示!"); Console.ReadKey(true); } public delegate void SayDelegate(string content);//定义一个委托 public void Say(string content,SayDelegate say) { say(content); } private void SayChinese(string content) { Console.WriteLine("中文:"+content); } private void SayEnglish(string content) { Console.WriteLine("英文:" + content); } }
public delegate void SayDelegate(string content);//定义一个委托 class Program { static void Main(string[] args) { SayManager manager = new SayManager(); manager.sayDelegate = Program.SayChinese; manager.Say("这是对委托的封装演示!"); Console.ReadKey(true); } private static void SayChinese(string content) { Console.WriteLine("中文:"+content); } private static void SayEnglish(string content) { Console.WriteLine("英文:" + content); } } class SayManager { public SayDelegate sayDelegate; public void Say(string content) { if (sayDelegate != null)//如果有方法注册了委托变量 { sayDelegate(content);//通过委托调用方法 } } }
事件:它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
public delegate void SayDelegate(string content);//定义一个委托 class Program { static void Main(string[] args) { SayManager manager = new SayManager(); manager.sayDelegate += Program.SayChinese; manager.Say("这是事件的演示!"); Console.ReadKey(true); } private static void SayChinese(string content) { Console.WriteLine("中文:" + content); } private static void SayEnglish(string content) { Console.WriteLine("英文:" + content); } } class SayManager { public event SayDelegate sayDelegate;//声明一个事件就是类似于声明一个进行了封装的委托类型的变量 public void Say(string content) { sayDelegate(content); } }
Action和Func:Action和Func是对传统委托封装的类,简化了传统委托的使用方式。
class Program { static void Main(string[] args) { Action actionA = MethodA;//使用Action表示一个没有返回值的委托 Action<string> actionB = MethodA;//使用Action表示一个没有返回值,有参数的委托 Func<string> funcA = MethodB;//使用Func表示一个有返回值的委托 Func<string, string> funcB = MethodB;//使用Func表示一个有返回值,有参数的委托 actionA(); actionB("Action演示!"); Console.WriteLine(funcA()); Console.WriteLine(funcB("Func演示!")); Console.ReadKey(true); } private static void MethodA() { Console.WriteLine("这个方法没有返回值!"); } private static void MethodA(string source) { Console.WriteLine("这个方法有参数,没有返回值!参数值:" + source); } private static string MethodB() { return "这个方法有返回值!"; } private static string MethodB(string source) { return "这个方法有参数,有返回值!参数值:"+source; } }
线程:每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。
线程是程序中一个单一的顺序控制流程.在单个程序中“同时”运行多个线程完成不同的工作,称为多线程。
线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文.多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定. 线程的运行中需要使用计算机的内存资源和CPU。
前台线程:只有所有的前台线程都关闭才能完成程序关闭。
后台线程:只要所有的前台线程结束,后台线程自动结束。
int num = 0; private void btnDemo_Click(object sender, EventArgs e) { Thread t1 = new Thread(Method);//因为Thread类的构造函数中的参数ThreadStart是委托,所以可以直接在这写方法名 Thread t2 = new Thread(Method); t1.IsBackground = true;//将线程设置为后台线程 t2.IsBackground = true; t1.Start(); t2.Start(); } private void Method() { Thread.Sleep(50); for (int i = 0; i < 10000; i++) { lock (this) { num++; lbDemo.SafeCall(() => { lbDemo.Text = num.ToString(); }); } } } public static class Extension { public static void SafeCall(this Control control, Action call) { if (control.InvokeRequired)//如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true; { control.Invoke(call);//在拥有此控件的基础窗口句柄的线程上执行委托 } else { call(); } } }
private void btnDemo_Click(object sender, EventArgs e) { Thread t = new Thread(Method); t.Start("演示线程实现带参数的方法!"); } private void Method(object source) { MessageBox.Show(source.ToString()); }
dynamic:dynamic是FrameWork4.0的新特性。dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再对类型进行检查,编译期默认dynamic对象支持你想要的任何特性。编译器对dynamic进行了优化,比没有经过缓存的反射效率快了很多。
[AttributeUsageAttribute(AttributeTargets.All, AllowMultiple = true, Inherited = false)]//可应用任何元素、允许应用多次、不继承到派生类 class GetUserInfo : System.Attribute { public string Name { get; set; } public int Age { get; set; } public GetUserInfo(string name, int age) { this.Name=name; this.Age=age; } public void Show() { MessageBox.Show(string.Format("姓名:{0},年龄:{1}", Name, Age)); } }
[GetUserInfo("张耕明", 28)] public class Demo{ }
dynamic demo = Attribute.GetCustomAttribute(typeof(Demo), typeof(GetUserInfo)); demo.Show();
Socket:英文原义是“孔”或“插座”,作为进程的通信机制,通常也称作“套接字”,用于描述IP地址和端口。是一个通信链的句柄。
在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。
三次握手(three-way handshaking):所谓的“三次握手”即对每次发送的数据量是怎样跟踪进行协商,使数据段的发送和接收同步,根据所接收到的数据量而确定的数据确认数及数据发送、接收完毕后何时撤消联系,并建立虚连接。
1.服务端开始监听端口(负责监听客户端连接信息)
2.客户端连接服务端指定端口(负责接收和发送服务端消息)
3.服务端监听到客户端连接,创建“套接字”(负责和客户端通信)
网络互连的七层框架:
OSI(Open System Interconnect):开放式系统互联。
FTAM---File Transfer Access and Management:文件传输访问和管理。VT---Virtualization Technology:虚拟化技术。
拓扑结构:计算机网络拓扑结构是指网络中各个站点相互连接的形式,在局域网中明确一点讲就是文件服务器、工作站和电缆等的连接形式。现在最主要的拓扑结构有总线型拓扑、星形拓扑、环形拓扑以及它们的混合型。
端系统:处在因特网的边缘部分就是在因特网上的所有主机,这些主机又称为端系统。
全双工:全双工是通讯传输的一个术语,通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。
半双工:信息能够在两个方向上进行发送,但不能同时发送的工作方式。
单工:单工就是在只允许甲方向乙方传送信息,而乙方不能向甲方传送。
虚拟终端VT:一种提供类似于Internet的Telnet协议的远程终端仿真的国际标准化组织(ISO)协议。在远程终端的用户,可以在远程计算机上运行应用程序,就像他们是坐在这台计算机前面一样
Telnet协议:该协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上操控远程主机的工作能力。
捎带确认:在计算机通信中,当一个数据帧到达的时候,接收方并不是立即发送一个单独的控制帧,而是抑制一下自己并且开始等待,直到网络层传递给它下一个分组。然后,确认信息被附在往外发送的数据帧上。实际上,确认报文搭了下一个外发数据帧的便车。这种“将确认暂时延迟以便可以钩到下一个外发数据帧”的技术称为捎带确认(piggybacking)。
第1层 物理层:处于OSI参考模型的最底层。物理层的主要功能是利用物理传输介质为数据链路层提供物理连接,以便透明的传送比特流。
第2层 数据链路层:在此层将数据分帧,并处理流控制。屏蔽物理层,为网络层提供一个数据链路的连接,在一条有可能出差错的物理连接上进行几乎无差错的数据传输。本层指定拓扑结构并提供硬件寻址。
第3层 网络层:本层通过寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。它包括通过互连网络来路由和中继数据。
第4层 传输层:常规数据递送,面向连接或无连接。为会话层用户提供一个端到端的可靠、透明和优化的数据传输服务机制。包括全双工或半双工、流控制和错误恢复服务。
第5层 会话层:在两个节点之间建立端连接。为端系统的应用程序之间提供了对话控制机制。此服务包括建立连接是以全双工还是以半双工的方式进行设置,尽管可以在第4层中处理双工方式。
第6层 表示层:主要用于处理两个通信系统中交换信息的表示方式。为上层用户解决用户信息的语法问题。它包括数据格式交换、数据加密与解密、数据压缩与恢复等功能。
第7层 应用层:OSI中的最高层。为特定类型的网络应用提供了访问OSI环境的手段。应用层确定进程之间通信的性质,以满足用户的需要。应用层不仅要提供应用进程所需要的信息交换和远程操作,而且还要作为应用进程的用户代理来完成一些为进行信息交换所必需的功能。它包括:文件传送访问和管理FTAM、虚拟终端VT、事务处理TP、远程数据库访问RDA、制造报文规范MMS、目录服务DS等协议。
TCP---Transmission Control Protocol(传输控制协议):TCP是一种连接导向的、可靠的、基于字节流的运输层(Transport layer)通信协议,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议。TCP层是位于IP层之上,应用层之下的传输层。TCP建立连接之后,通信双方都可以进行数据的传输,它是全双工的,在保证可靠性上,采用超时重传和捎带确认机制。
服务端: Thread threadWatch = null;//负责监听客户端连接请求的线程 Socket socketServer = null;//负责监听的套接字 private void btnBeginListen_Click(object sender, EventArgs e) { //创建服务端用于监听的套接字,参数(使用IP4寻址协议、使用流式连接、使用TCP协议传送数据) socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); string localIP= Dns.GetHostAddresses(Dns.GetHostName()).Where(n => n.AddressFamily == AddressFamily.InterNetwork).First().ToString();//获取本地的IP4地址 IPAddress address = IPAddress.Parse(localIP);//设置IP地址 IPEndPoint endPoint = new IPEndPoint(address, int.Parse(tBoxPort.Text.Trim()));//创建包含IP和端口的网络节点 socketServer.Bind(endPoint);//绑定套接字到唯一的IP和端口上 socketServer.Listen(10);//设置监听队列的长度,服务器配置好可设置较大 threadWatch = new Thread(Accept);//使用后台线程监听客户端的连接请求 threadWatch.IsBackground = true; threadWatch.Start(); } private void Accept() { Socket socketConn = socketServer.Accept();//开始监听客户端连接请求,会阻断线程 MessageBox.Show("监听到客户端的连接请求,连接成功!"); } 客户端: private void btnConnect_Click(object sender, EventArgs e) { IPAddress address = IPAddress.Parse(tBoxIP.Text.Trim()); IPEndPoint endPoint = new IPEndPoint(address, int.Parse(tBoxPort.Text.Trim())); Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socketClient.Connect(endPoint);//连接服务端 }
LINQ:语言集成查询(Language INtegrated Query)是一组用于C#和Visual Basic语言的扩展。它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据。 LINQ定义了大约40个查询操作符,如select、from、in、where以及order by(C#中)。
Lambda表达式:Lambda表达式是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。
Lambda在基于方法的LinQ查询中用作标准查询运算符方法的参数。
int[] intArr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 9 }; string task = intArr.Take(5).Dump();//获取从序列开头指定数量的连续元素 string skip = intArr.Skip(5).Dump();//跳过序列中指定数量的元素,返回剩余元素 string reverse = intArr.Reverse().Dump();//反转序列中元素的顺序 string orderBy = intArr.OrderBy(n => n).Dump();//升序排序 string orderByDescending = intArr.OrderByDescending(n => n).Dump();//降序排序 int first = intArr.First();//返回序列的第一个元素 int last = intArr.Last();//返回序列的最后一个元素 int max = intArr.Max();//获取序列最大值 int min = intArr.Min();//获取序列最小值 int sum = intArr.Sum();//获取序列的总和 int count = intArr.Count();//获取序列的元素数量 int element = intArr.ElementAt(1);//获取指定索引的元素 bool contains = intArr.Contains(10);//检查序列是否包含指定元素 bool any = intArr.Any();//检查序列是否有元素 string distinct = intArr.Distinct().Dump();//返回非重复元素 string where = intArr.Where(n => n > 5).Dump();//获取大于5的数字 StringBuilder strBuilder = new StringBuilder(); strBuilder.AppendLine(string.Format("Task:{0},Skip:{1},Reverse:{2}",task,skip,reverse)); strBuilder.AppendLine(string.Format("OrderBy:{0},OrderByDescending:{1}", orderBy, orderByDescending)); strBuilder.AppendLine(string.Format("First:{0},Last:{1}", first, last)); strBuilder.AppendLine(string.Format("Max:{0},Min:{1},Sum:{2},Count:{3}",max,min,sum,count)); strBuilder.AppendLine(string.Format("ElementAt:{0},Contains:{1},Any:{2}", element, contains,any)); strBuilder.AppendLine(string.Format("Distinct:{0},Where:{1}", distinct, where)); MessageBox.Show(strBuilder.ToString()); public static partial class Extension { public static string Dump(this IEnumerable<int> source) { string temp = ""; foreach (int i in source) { temp += i.ToString() + " "; } return temp; } }
可变参数用法:方法的参数个数是可以改变的,关键字params。可变参数数组必须是最后一个。
class Program { static void Main(string[] args) { Program.Method("张耕明", "混世魔王", "万人往"); Console.ReadKey(true); } static void Method(string name, params string[] nickname) { Console.WriteLine("我的名字是:" + name); foreach (string str in nickname) { Console.WriteLine("绰号:" + str); } } }
字符串: String类型的值是只读的,具有不可变性。每次对string变量的修改都会在内存中重新分配一块区域。string类型的值在内存中分配的区域不可变,变的只是变量名指向的位置。String对象是Char对象的有序集合,用于表示字符串。String对象的值是该有序集合的内容,并且该值是不可变的。对String类的任何改变都是返回一个新的String类对象,会在内存中开辟新的空间。String是.NET Framework里面的String,小写的string是C#语言中的string ,最终通过编译后,小写的string会变成大写的String。
String类常用方法:ToLower()、ToUpper()、Trim()、Equals()、Split()、Replace()、Substring()、Contains()、StartsWith()、IndexOf()
string strDemo = "Good good study! day day up! "; Console.WriteLine("转小写:{0}", strDemo.ToLower());/*并不是改变了字符串的内容,而是指向了生成的新的字符串*/ Console.WriteLine("转大写:{0}", strDemo.ToUpper()); Console.WriteLine("去除两边的空白:{0}", strDemo.Trim()); Console.WriteLine("对字符串进行比较:{0}", strDemo.Equals("Good good study! day day up! ",StringComparison.OrdinalIgnoreCase));/*不区分大小写*/ string[] str1 = strDemo.Split(new char[] { '!' }, StringSplitOptions.RemoveEmptyEntries);//以'!'将字符串分割开,放入另一个数组,移除空字符串 foreach (string s in str1) { Console.WriteLine(s); } strDemo = strDemo.Replace("Good good study", "好好学习");//替换字符串 Console.WriteLine(strDemo); Console.WriteLine(strDemo.Substring(7));//从第7号位索引开始往后截取字符串 Console.WriteLine(strDemo.Substring(0, 6));//从0号位索引开始截取6个字符 bool bl = strDemo.Contains("好好学习");//判断字符串中是否含有指定的字符或字符串 bl = strDemo.StartsWith("好好");//判断字符串是否以指定的字符串开头 int index = strDemo.IndexOf("Good");//获得指定字符或字符串在整个字符串中的第一个索引位置,如果不存在返回-1 strDemo = strDemo.Insert(0, "插入字符串!"); Console.WriteLine(strDemo); strDemo = strDemo.Remove(1);//删除从指定索引开始往后的字符串 Console.WriteLine(strDemo); Console.ReadKey(true);
可变字符串类StringBuilder:在需要对字符串进行大量重复修改的情况下使用可以提升程序的性能。
StringBuilder strBuilder = new StringBuilder();//实例化StringBuilder类的对象 Console.WriteLine(strBuilder.Capacity);//初始容量16 Console.WriteLine(strBuilder.Append("我人有的和主产不为这工要在地一上是中国同经以发了民"));//追加字符串到对象的末尾 Console.WriteLine(strBuilder.Capacity);//此时容量32,当容量超出会自动扩容 Console.WriteLine(strBuilder.Insert(0, "大家好才是真的好!"));//插入内容到对象的指定位置 Console.WriteLine(strBuilder.Remove(0, 2));//删除从指定索引开始的指定数量的字符 Console.WriteLine(strBuilder.Replace("才", "!"));//替换指定的内容 Console.WriteLine(strBuilder.MaxCapacity);//StringBuilder类的对象最大的容量 Console.ReadKey(true);
方法的ref、out参数:
class Program { static void Main(string[] args) { int age = 10, salary = 5000000; Program.Use_ref(ref age);//加了ref,变量必需赋初值 Program.Use_out(out salary);//加了out,变量可以不赋初值 Console.WriteLine("age:" + age); Console.WriteLine("salary:" + salary); Console.ReadKey(true); } static void Use_ref(ref int age)/*不加ref传的是Copy值,加ref传的是引用值*/ { age++; /*复制过来的值改变后不会影响到原来的值*/ } static void Use_out(out int salary) { //Console.WriteLine(salary);/*传进来的参数会被视为未赋值的*/ salary = 10000; //使用加out的参数,方法内部必须给参数赋值 } }