黑马程序员C#学习笔记
黑马程序员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)
{
https://www.360docs.net/doc/fa8223203.html, = 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}", https://www.360docs.net/doc/fa8223203.html,, 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}", https://www.360docs.net/doc/fa8223203.html,, 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;
https://www.360docs.net/doc/fa8223203.html, = name;
}
static void Main()
{
Program p = new Program();//使用没有参数的构造函数
Program p1 = new Program("001", "张耕明");//使用有参数的构造函数
Console.WriteLine(https://www.360docs.net/doc/fa8223203.html,);
Console.WriteLine(https://www.360docs.net/doc/fa8223203.html,);
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;
///
///构造函数,初始化矩形的宽和高
///
///矩形的宽
///矩形的高
public Rectangle(double x, double y)
{
_width = x;
_height = y;
}
///
///计算矩形的面积
///
///
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;
///
///浅拷贝
///
public void ShallowCopy()
{
深拷贝和浅拷贝 instance1 = new深拷贝和浅拷贝();
深拷贝和浅拷贝 instance2 = instance1;//浅拷贝,拷贝对象和源对象都指向同一个实例
instance1._shallow = "Hello C#!";
instance2._shallow = "Shallow Copy!";
Console.WriteLine("浅拷贝:{0}、{1}", instance1._shallow, instance2._shallow);
}
///
///深拷贝
///
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
使用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 };//类的一个对象
修饰符:
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
{
public T Info { get; set; }
public void Method()
{
Console.WriteLine(Info);
}
}
class Program
{
static void Main(string[] args)
{
WideClass
https://www.360docs.net/doc/fa8223203.html, = "泛型演示!";
instance.Method();
Console.ReadKey(true);
}
}
枚举:枚举(enum)是值类型的一种特殊形式,枚举类型有名称、基础类型和一组字段。基础类型必须是一个有符号(或无符号)整数类型,如果没有显示的声明基础类型,则使用Int32。字段是静态文本字段,其中的每一个字段都表示常数。
程序中使用枚举的限制:不能定义自己的方法;不能实现接口;不能定义属性和事件。
位域支持的运算符:
1、“|”表示两边求并集(元素相加,相同元素只出现一次)。
2、“&”表示两边是否其中一个是另外一个的子集,如果是返回子集,否则返回0。
3、“^”表示从两者的并集中去除两者的交集。
4、“&(~ )”从组合状态中去掉一个元素。
///
///枚举类型可以使用Flags位域标志,表示这些枚举类型可以作为位域(即一组标志)处理。
///C#位域主要用于.Net里面对于某一个事物有多种混合状态时使用,为了更好的实现混合状态,我们可以在枚举中加入位域标签。
///
[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
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
Func
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();
}
}
}