类的概念

摘自: https://www.cnblogs.com/2016Study/p/5514038.html
1、类
语法:

[public] class 类名
{
字段;
属性;
方法;
}

写好了一个类之后,我们需要创建这个类的对象,
那么,我们管创建这个类的对象过程称之为类的实例化。
使用关键字 new.

this:表示当前这个类的对象。
类是不占内存的,而对象是占内存的。
还有一个作用:this 显式的调用自己的构造函数,提示:1) base是显式的调用父类的构造函数。 2)base也可以用来调用父类中的重名方法
1.子类继承父类的方法,属性, 但不能继承构造函数和私有字段。

params的用法

//string s = string.Join("|", new int[] { 1, 2, 3, 4, 5 });
//string s = string.Join("|", new bool[] { true,false,false,true,false });
string s = string.Join("|", 1, 3.14, true, "c", "张三");
string s = string.Join(
Console.WriteLine(s);

  • params 可变参数数组,可以放数组,也可以放单个元素。
    将实参列表中跟可变参数数组类型一致的元素都当做数组的元素去处理。
    params 可变参数必须是形参列表中的最后一个元素。

5、方法的重载

概念:方法的重载指的就是方法的名称相同,但是参数不同
参数不同, 分为两种情况:

  1. 如果参数的个数相同,那么参数的类型就不能相同。
  2. 如果参数的类型相同,那么参数的个数就不能相同。
    ***方法的重载跟返回值没有关系。

基础的第九天

  • 值类型与引用类型
    区别:
    1、值类型与引用类型在内存上的地方不一样。
    2、在传递值类型与传递引用类型的时候,传递的方式不一样。
    值类型我们称之为值传递,引用类型我们称之为引用传递。
    我们学的值类型与引用类型:
    值类型:int、double、bool、char、decimal、struct、enum
    引用类型: string、自定义类型、数组、集合、object、接口

值类型在复制的时候,传递的是这个值的本身。
引用类型在复制的时候,传递的是对这个对象的引用。(字符串不一样,字符串有不可变性,所以s1="张三"; s2=s1, s2="李四", s1与s2的值还是不同的。)

存贮:
值类型的值是存储在内存的栈当中。
引用类型的值是存储在内存的堆当中。

  • 字符串
    1)字符串的不可变性
    当你给一个字符串重新赋值之后,老值并没有销毁,而是重新开辟一块空间存贮新值。
    当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。
    2)我们可以将字符串看做是char类型的一个只读数组。

  • ?可空类型与可空操作
    可空类型
    可空类型只针对值类型而不能用于引用类型,因为引用类型本身就是可空类型,对于隐式类型var,不能声明可空类型。
    可空类型实际上是声明了一个泛型System.Nullable<T>结构类实例,T?后缀就是创建一个System.Nullable<T>的简写。

  • ??操作符
    对于可空类型可以使用??操作符,例如
    Int K=test??100,其中test可能为空,若test为空时则将100赋值给K,若不为空则将test赋值给K

  • 继承
    1.子类继承父类的方法,属性, 但不能继承私有字段。
    不能继承构造函数,但是子类会默认的调用父类无参数的构造函数,创建父类对象,让子类可以使用父类中的成员。
    所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的就被干掉了,子类就调用不到了,所以子类会报错。
    解决办法:
    1)在子类中重新写一个无参数的构造函数
    2)在子类中显式的调用父类的构造函数,使用关键字:base()

  • 编码
    将字符串以怎样的形式保存为二进制
    乱码:产生的原因: 就是你保存这个文件所采用的编码,跟你打开这个文件所采用的编码格式不一样。

================================================
摘自 https://www.icode9.com/content-4-1161156.html

在类前面加static就是静态类,在方法或者字段和属性前面加static就代表是静态成员
反之不加就是非静态类或者非静态成员(实例成员)。

静态和非静态的区别
1)、在非静态类中,既可以有实例成员(非静态成员),也可以有静态成员

2)、在调用实例成员的时候,需要使用对象名.实例成员;
在调用静态成员的时候,需要使用类名.静态成员名;

总结:
静态成员必须使用类名去调用,而实例成员使用对象名调用。
静态函数中,只能访问静态成员,不允许访问实例成员。
实例函数中,既可以使用静态成员,也可以使用实例成员。
静态类中只允许有静态成员,不允许出现实例成员。

使用:
1)、如果你想要你的类当做一个"工具类"去使用,这个时候可以考虑将类写成静态的。
2)、静态类在整个项目中资源共享。
只有在程序全部结束之后,静态类才会释放资源。

堆 栈 静态存储区域
释放资源。GC Garbage Collection垃圾回收器

关于占用内存
静态的东西在类加载的时候,会在内存中开辟空间,实例成员不会,所以在静态类或者方法里无法访问到实例成员,所以静态方法里面只能访问静态字段或者属性。
静态类已分配固定的内存空间,非静态类在每new一个新对象要在内存中重新开辟空间,所以静态类不能实例化
静态类会在程序初始化运行时创建完所有字段和方法,而非静态类则会在实例化时创建所有字段和方法
类是不占内存的,只有对象是占内存的,静态类也是占内存的

内存有
堆,栈,静态存储区域(静态资源全部都放在静态存储区域,在项目中任意地方都可以访问)
======================================================

2、属性
属性的作用就是保护字段、对字段的赋值和取值进行限定。
属性的本质就是两个方法,一个叫get()一个叫set()。
既有get()也有set()我们诚之为可读可写属性。
只有get()没有set()我们称之为只读属性
没有get()只有set()我们称之为只写属性

2.1 ####方法
A. 引用类型的对象, 按值传送到方法时, 这个方法接收的不是对象本身, 而是指示这个对象位置的自变量, 值会有变化

B. 真正想改值

3、访问修饰符
public:公开的公共的,在哪都能访问。
private:私有的,只能在当前类的内部进行访问,出了这个类就访问不到了。

4、
当我们创建好一个类的对象后,需要给这个对象的每个属性去赋值。
我们管这个过程称之为对象的初始化。

5、静态方法和非静态方法的区别
1)、在非静态类中,既可以有实例成员,也可以有静态成员。
2)、在调用实例(非静态)成员的时候,只能使用对象名.实例(非静态)成员;
在调用静态成员的时候,只能使用类名.静态成员名;
总结:静态成员必须使用类名去调用,而实例成员(非静态)使用对象名调用。
静态函数中,只能访问静态成员,不允许访问实例(非静态)成员。
实例函数中,既可以使用静态成员,也可以使用实例(非静态)成员。
静态类中只允许有静态成员,不允许出现实例成员。

使用:
1)、如果你想要你的类当做一个"工具类"去使用,这个时候可以考虑将类写成静态的。
2)、静态类在整个项目中资源共享。
只有在程序全部结束之后,静态类才会释放资源。

堆 栈 静态存储区域

释放资源。GC Garbage Collection垃圾回收器

6、构造函数
作用:帮助我们初始化对象(给对象的每个属性依次的赋值)
构造函数是一个特殊的方法:
1)、构造函数没有返回值,连void也不能写。
2)、构造函数的名称必须跟类名一样。

创建对象的时候会执行构造函数
构造函数是可以有重载的。


类当中会有一个默认的无参数的构造函数,当你写一个新的构造函数之后,不管是有参数的还是
无参数的,那个默认的无参数的构造函数都被干掉了。

7、new关键字
Person zsPerson=new Person();
new帮助我们做了3件事儿:
1)、在内存中开辟一块空间
2)、在开辟的空间中创建对象
3)、调用对象的构造函数进行初始化对象
后面补充:

  • 还有一个作用是用在:当子类函数与父类函数同名时,在子类的函数返回值前加new, 作为隐藏从父类那里继承过来的成员。

8、this关键字
1)、代表当前类的对象
2)、在类当中显式的调用本类的构造函数 :this base调用父类的构造函数

== C#和.Ne学习第十一天

1、里氏转换
1)、子类可以赋值给父类 (如果有一个地方需要一个父类作为参数,我们可以给一个子类代替).
2)、如果父类中装的是子类对象,那么可以讲这个父类强转为子类对象。

2、
子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。

3、
is:表示类型转换,如果能够转换成功,则返回一个true,否则返回一个false
as:表示类型转换,如果能够转换则返回对应的对象,否则返回一个null

4、protected
受保护的:可以在当前类的内部以及该类的子类中访问。

5、ArrayList集合的长度问题
每次集合中实际包含的元素个数(count)超过了可以包含的元素的个数(capcity)的时候,
集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。

6、Hastable 键值对集合 字典 孙 sun---孙
在键值对集合当中,我们是根据键去找值的。
键值对对象[键]=值;
*****:键值对集合当中,键必须是唯一的,而值是可以重复的


7、foreach循环

8、编码格式
产生乱码的原因就是因为你保存这个文件的编码格式跟你打开这个文件的编码格式不一样。

9、文本文件
拖到txt文件中,还能看得懂的文件就是文本文件。

装箱、拆箱
装箱:就是将值类型转换为引用类型。
拆箱:将引用类型转换为值类型。
看两种类型是否发生了装箱或者拆箱,要看,这两种类型是否存在继承关系。

==Net基础班第十三天
1、c#中的访问修饰符
public :公开的公共的
private:私有的,只能在当前类的内部访问
protected:受保护的,只能在当前类的内部以及该类的子类中访问。
internal:只能在当前项目中访问。在同一个项目中,internal和public的权限是一样。
protected internal:protected+internal

1)、能够修饰类的访问修饰符只有两个:public、internal。
如果类前面不加修饰符,默认为internal。
2)、可访问性不一致。
子类的访问权限不能高于父类的访问权限,会暴漏父类的成员。

2、设计模式
设计这个项目的一种方式。

3、简单工厂设计模式

4、值类型在复制的时候,传递的是这个值得本身。
引用类型在复制的时候,传递的是对这个对象的引用。

5、序列化:就是将对象转换为二进制
反序列化:就是将二进制转换为对象
作用:传输数据。
序列化:
1)、将这个类标记为可以被序列化的。

6、partial部分类

7、sealed密封类
不能够被其他类继承,但是可以继承于其他类。
8、接口
[public] interface I..able
{
成员;
}

练习:

/*

  • 麻省会飞 鹦鹉会飞 鸵鸟不会飞 企鹅不会飞 升飞机会飞
  • 用多态来实现
  • 虚方法、抽象类、接口
    */

//多态: 一个对象能够表现出多种的状态、类型。
//实现多态有三种手段: 1、虚方法 2、抽象类 3、接口
*选择虚方法还是抽象类实现多态,看这个父类要不要实例化,要实例化就选虚方法。如果父类本身没有什么意义,就用抽象类。
什么时候使用虚方法来实现多态?
什么时候使用抽象方法来实现多态?
什么时候使用接口来实现多态?
这几个类中可以抽象出一个父类,并且父类可以写出这几个类共有的方法,然后你不知道如何写这个方法,用抽象类(比如说狗叫,猫叫可以描述,动物叫描述不出来),
反之,抽象出来的这个父类,这个方法,可以写,并且还需要创建这个父对象,用虚方法(比如真鸭子嘎嘎叫,木鸭子吱吱叫,橡皮鸭子唧唧叫)
再之,这几个类里面, 你根本找不出父类,但是它们都有一个共同的行为,共同的能力,用接口,(比如鸟会飞,飞机也会飞)

子类会默认调用父类无参的构造函数,如果父类写了有参的构造函数,则子类就调用不了父类的无参的构造函数,如果要调用有两个办法:
1.在父类中再添加一个无参的构造函数。
2. 在子类中显式调用父类中的有参的构造函数。

点击查看代码
public class Person
{
   public string Name {get;set;}
   public Person(name)
   {
      this.Name = name;
   }

   public void SayHello()
   {
      Console.WriteLine("大家好,我叫{0},我是人类", this.Name);
   }
}

public class Chinese:Person
{
   public Chinese(string name):base(name)
   {
      //这里不用再写this.Name = name, 因父类已经赋值了。
   }

   public void SayHello()
   {
       Console.WriteLine("大家好,我叫{0},我是中国人", this.Name);
   }
}

当子类的方法名与父类的方法名相同时,只能调用子类自己的方法,但代码的警告(子类默认会隐藏父类同名的方法)可以在子类的方法返回值前面加 new 关键字去掉。
//如果子类中的方法名与父类的方法名一样, 那么子类会默认的将父类的方法给隐藏掉。
//解决办法两个:1. 使用new关键字 2.使用虚方法
//new的作用:隐藏子类从父类继承过来的方法。

//使用虚方法的步骤:
1、将父类的方法标记为virtual 表示这个父类的方法可以被子类重写
2、将子类的方法标记为override 表示重写父类的方法
3、虚方法,抽象类的方法实现要在前面加override, 接口的方法实现不需要加override。

  • 多态:让父类的类型装的是子类的对象
    //当父类不知道写什么东西的时候,可以考虑使用抽象类。
    //抽象类当中可以有属性、方法
    //抽象类不能被实例化,抽象类中的抽象方法不允许有方法体
    //如果一个子类继承了一个抽象类的父类,那么这个子类必须实现这个抽象父类当中的所有抽象成员,除非这个子类也是抽象类。

//抽象父类当中的抽象方法,既可以有返回值,也可以有参数。
//但是,子类重写父类的方法必须返回值和参数要同父类的一样。

//1.抽象类前必须用abstract标记,抽象类中的抽象成员也需要用abstract标记。
//2.抽象成员必须存在于抽象类当中
//3.抽象类当中可以有非抽象的成员 (但是有什么用呢?有用的, 继承的子类可以点出来。)
//4.抽象类只能用作基类,无法实例化。(用作子类没什么意思,又不能点出来成员)

//接口
//类实现所有的接口,都可以显式实现。
//显式实现接口,就是为了解决继承多个接口时有方法重名的问题。
//第一个方法是普通实现,后面如果同名的方法则需要显式实现

//接口是一种规范。
//只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员。

为了多态,接口不能被实例化。接口与抽象类都不能够被实例化。
也就是说, 接口不能new(不可创建对象)。

//接口中的任何成员都不能显式地加“访问修饰符”,接口中的成员访问修饰符默认为public,不能更改。
(默认为public)接口中的成员不能有任何实现("光说不做",只是定义了一组未实现的成员)。
接口中只能有方法、属性、索引器(其实反编译之后就是一个item属性 返回值类型 this[int index]😉、事件,==>其实这几样归根到底还是方法。不能有"字段"。
接口与接口之间可以继承,并且可以多继承。
一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。

class MyClass:A,IA {} 因为类是单继承的。

当一个抽象类实现接口的时候,如果不想把接口中的成员实现,可以把该成员实现为abstract。(抽象类也能实现接口,用abstract实现接口,只能通过接口变量来调用(因为显式实现接口后成员为private)。

编程优先:
接口-->抽象类-->虚方法父类-->父类-->具体类(在定义方法参数、返回值、声明变量的时候能用抽象就不要用具体。)
能使用接口就不用抽象类,能使用抽象类就不用类,能用父类就不用子类。
避免定义“体积庞大的接口”、“多功能接口”,只把相关联的成员定义到一个接口中(尽量在接口中少定义成员)-->单一职责原则

==
数组一旦声明, 它的长度与类型就已经固定,而集合则是长度可变, 并且可以存贮任意类型的数据到集合中
有四种集合类型:ArrayList Hashtable List Dictionary<T,T>
List长度任意, 类型固定

由于装箱和拆箱的原因, ArrayList及Hashtable已经比较少用, 主要用List,Dictionary<T,T>

*字符串可以看作是char类型的只读数组。
*字符串可以用ToCharArray()转换为字符数组
*字符数组转变为字符串 new string(char[] chs)
Split(new char[]{'要分割的字符',StringSplitOptions.RemoveEmptyEntries): 分割字符串,返回一个字符串类型的数组。
例如: Split(new char[]{'-',' ','},StringSplitOptions.RemoveEmptyEntries)

  • 字符串还有这样的用法
string[] mon = "7月".split(new char[]{'月',StringSplitOptions.RemoveEmptyEntries);
string smon = "12月".split(new char[]{'月',StringSplitOptions.RemoveEmptyEntries)[0];

*也可以将集合转成数组
List list = new List();
list.ToArray()

List list1 = new List();
list1.ToArray()
*返回什么类型的数组取决于集合里装的是什么类型

Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "张三");
dic.Add(2, "李四");
dic.Add(3, "王五");
dic[4] = "赵六";
dic[2] = "哈哈";
//lamda表达式
foreach (KeyValuePair<int,string> item in dic)
{
    Console.WriteLine("{0}-------{1}",item.Key,item.Value);
}
//foreach (var item in dic.Keys)
//{
//    Console.WriteLine("{0}---{1}",item,dic[item]);
//}
Console.ReadKey();

//这种方法不行, 只能取到键值, 如果Dictionary的键值类型不是int, 还会出错
//for (int i = 0; i < dic.Count; i++)
//{
//    Console.WriteLine(dic[i]);
//}

================================ 2014年 .Net基础班 start================
==第十一天
1、集合
集合相对于数组的好处
1)、长度可变
2)、可以添加任意类型的元素
不好的地方:
从集合中取值的时候必须要不停的做类型转换

2、集合的方法
Add():添加单个元素
AddRange():添加集合
list.AddRange(new int[]{1,2,3,4,5}) //给集合添加集合

Clear():清空集合中所有的元素
Remove():删除集合中的元素,括号里写谁,集合就删谁
RemoveAt():根据索引去删除集合中的元素
RemoveRange():删除一定范围内的元素
Insert():向集合的指定位置插入一个元素
InsertRange():向集合的指定位置插入一个集合
Contains():判断集合中是否包含某个元素
Sort():升序排列

3、集合的两个属性
Count :获取这个集合中实际包含的元素个数
Capcity:获取这个集合可以包含的元素个数

4、var推断类型
var可以通过变量的值来推断出来这个变量的类型。

5、Hashtable
在键值对集合中,键一定是唯一的,不能重复的,而值是可以重复的。
有两种方法向字典添加数据:
1) 用add方法
2) 直接用键 ht[5] = "李四"
注意第二种方法不检查键是否重复,如果重复相当把新值代替旧值。

常用集合

  • "类似数组"集合:ArrayList、List
  • "键值对集合"(哈希表集合):Hashtable、Dictionary<K,V>
  • "堆栈集合":Stack、Stack(LIFO) Last In First Out
  • "队列集合":Queue、Queue(FIFO) First In First Out
  • "可排序键值对集合":(插入、检索没有"哈希表"集合高效)
    • SortedList、SortedList<K,V>(占用内存更少, 可以通过索引访问)
    • SortedDictionary<K,V> (占用内存更少,没有索引,但插入,删除元素的速度比SortedList快)
    • Set集合:无序、不重复。HashSet, 可以将HashSet类视为不包含值的Dictionary集合。与List类似。SortedSet(Net4.0支持,有序无重复集合)。
    • "双向链表"集合:LinkedList, 增删速度快。

6、编码 GB2312---GBK
将字符串以什么样的形式保存为二进制,这个就是编码,UTF-8、ASCII、Unicode等,

产生乱码的原因:
我保存这个文件的时候所采取的编码格式跟我打开的时候所采取的编码格式不一致。

7、文本文件
拖到txt里还能看得懂的是文本文件。

8、绝对路径、相对路径
绝对路径:根据这个文件的路径,直接能在你的电脑上找到。
相对路径:相对于你应用程序的路径。

总结:可以将集合看作为"长度可变的,具有很多方法的数组"。

==
0326.Net基础班第十二天

1、文件流
用来操作大文件
FileStream:用来操作字节的

StreamWriter StreamReader:用来操作字符的

2、由于我们在使用FileStream的过程当中,经常会忘记写Close() Dispose(),
所以微软提供给我们更为简单的方法帮助我们关闭流和释放流所占用的资源。
就是将创建FileStream对象的过程写在using(){}当中

3、装箱、拆箱
装箱:值类型-------->引用类型
拆箱:引用类型------>值类型
值类型: int double decimal enum struct bool char
引用类型: 数组 集合 自定义类型 string object Interface

string s = "123";
int i = Convert.ToInt32(s); //拆箱?这个地方没有发生任何的装箱或者拆箱

int n = 10;
IComparable i = n; //值类型 -->引用类型 装箱

我们看两个类型之间是否发生装箱或者拆箱,首先要确定这两个类型之间是否存在继承关系。
如果存在,则有可能会发生拆箱或者装箱,
如果不存在继承关系,则不会发生装箱或者拆箱。
注意:装箱的时候使用什么类型装的箱,拆箱的时候也得使用对应的类型进行拆箱。(里氏转换)

ArrayList listArr = new ArrayList();
//List<int> list = new List<int>();
StopWatch sw = new StopWatch();
sw.Start();
for(i=0;i<1000000;i++)
{
    listArr.Add(i);  //装箱  2秒多
    //list.Add(i);      //零点几秒
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
Console.ReadKey();

//变量: 变量可以重新被赋值, 常量不能重新赋值。
声明常量的语法:
const 变量类型 变量名=值

与变量相比只是多了一个const

6.枚举 作用:规范我们的开发

语法:

[public] enum 枚举名
{
   值1,
   值2,
   …………
}

public(有中括号表示可省略):访问修饰符。公开的公共的,那都可以访问。
enum: 关键字,声明枚举的关键字。
枚举名:要符合Pascal命名规范。即每个单词首字母大写

//将枚举声明到命名空间的下面,类的外面,表示这个命名空间下,所有的类都可以使用这个枚举。
枚举就是一个变量类型,与int, double, string, decimal 一样。
只是枚举声明、赋值、使用的方式跟那些普通的变量类型不一样。

//声明了一个枚举,类型为Gender

public enum Gender
{
   男,
   女
}

//变量类型 变量名=值;
int n = 10;
Gender gender='男'; -->这是错误的,左边的类型是Gender, 右边是char类型,类型不同,不能赋值。
Gender gender=Gender.男; -->这个正确。
//枚举类型的值是由枚举类型点出来的。

//枚举类型默认可以与int类型互相转换 枚举类型与int类型是兼容的
QQState state = QQState.OnLine;
int n = (int)state; (int)等转换的类型,state要转换的值。
枚举类型默认从0开始, 也可以改由其它数值开始。

//所有的类型都能够转换成string类型。
//将字符串类型转换为枚举类型:

public enum QQState
{
    OnLine=1,
    OffLine,
    Leave=5,
    Busy,
    QMe
}

将字符串转换为枚举类型:
string s = "0";
//将s转换为枚举类型
QQState state = (QQState)s; -->不正确,不能将不兼容的两种类型互转。强转要求两种类型是兼容的。

//将字符串转换为int类型有三种方法:
Convert.ToInt32() int.parse() int.TryParse() -->其中Convert.ToInt32的内部也是调用parse方法完成,所以前两个方法是一类的。
//调用Parse()方法的目的就是为了让它帮助我们将一个字符串转换成对应的枚举类型。

QQState state = (QQState)Eumm.parse(typeof(QQState),s) -->正确。

//Parse()要完成需要我们提供一些条件,提供的条件就是这个方法所需要的一些参数。

//我们可以将一个枚举类型的变量跟int类型和string类型互相转换。
枚举类型默认是跟int类型相互兼容的,所以可以通过强制类型转换的语法互相转换。当转换一个枚举中没有的值的时候,不会抛出异常,而是直接将数字显示出来。

枚举同样也可以与string类型互相转换,如果将枚举类型转换成string类型,则直接调用ToString().
如果将字符串转换成枚举类型则需要下面这样一行代码:
(要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型),"要转换的字符串");
如果转换的字符串是数字,则就算枚举中没有, 也不会抛出异常。
如果转换的字符串是文本,如果枚举中没有,则会抛出异常。

  1. 结构
    可以帮助我们一次性声明多个不同类型的变量
    语法:
[public] struct 结构名
{
    成员;
}
  1. 结构: 一次性声明多个不同类型的变量
    语法:
    [public] struct 结构名
    {
    结构成员;
    }

结构成员其实应该叫做字段, 不叫变量, 字段最好在前面加下画线。
字段跟变量有一个配制的区别:字段可以存储多个值,而变量只能存储一个值。

  1. 数组 //一次性存储或声明多个相同类型的变量
    数组类型[] 数组名=new 数组类型[数组长度];
    有4种声明数组的方式:
    int[] nums = new int[10]; //这种声明数组的方式只是确定了数组的类型和长度,并没有赋值
    int[] nums = {1,2,3,4,5}; //这种声明数组的方式在声明的时候,就给数组赋值了
    int[] nums = new int[4] {1,2,3,4};
    int[] nums = new int[] {1,2,3,4};
    后面这两种有点画蛇添足了……。
  • 对象初始化器
    Person p = new Person(){Name = "张三",Age=10,Gender='男'};
  • 集合初始化器
    List list = new List(){1,2,3,4,5,6,7,8,9,0};

==================2014年 .Net基础班 end==============================
==================.Net基础加强第七天 start ============================

07Net基础加强第七天-自定义泛型

点击查看代码
//(1)泛型类
public class MyClass<HL,K,V,W,Y,Z>
{
	public void SayHi(HL arg)
	{
		Console.WriteLine(arg);
	}
}

//(2)泛型方法
public class Class1
{
	public void SayHello<T>(T msg)
	{
		Console.WriteLine(msg);
	}
}

//(3)泛型接口
public interface IFace<T>
{
	T SayHi();
	void SayHello(T msg);
}
//(4)泛型委托

//实现泛型接口的时候有两种情况
//1. 普通类来实现泛型
public class Class2 : IFace<string>
//public class Class2 : IFace<T> //这里不能这样写, 因为这个T没人给,因为这里是使用接口,不是定义接口,
//使用接口必须传一个类型过来(要么传实际类型,要么象泛型类实现泛型接口一样,从类里传一个参数过来)。
{
	public void SayHello(string msg)
	{
		throw new NotImplementedException();
	}

	public string SayHi()
	{
		throw new NotImplementedException();
	}
}
//2、泛型类实现泛型接口
public class Class3<U> : IFace<U>	//IFace<U>中的U已经在类中声明了, 所以可以直接使用U
{
	public void SayHello(U msg)
	{
		throw new NotImplementedException();
	}

	public U SayHi()
	{
		throw new NotImplementedException();
	}
}
  • 泛型有什么作用:算法重用。

  • 完整的实现一个泛型的例子

点击查看代码


using System;
namespace _05Test
{
	class Program
	{
		static void Main(string[] args)
		{
			MyCalss mc = new MyCalss();
			mc[0] = "aa";
			mc[1] = "bb";
			Console.WriteLine(mc[0]);
			Console.WriteLine(mc[1]);
			//使用泛型
			Console.WriteLine("==================222===================");
			MyCalss2<string> mc2 = new MyCalss2<string>();
			mc2[0] = "aa";
			mc2[1] = "bb";
			Console.WriteLine(mc[0]);
			Console.WriteLine(mc[1]);
			Console.WriteLine("=================333=====================");
			MyCalss2<int> mc3 = new MyCalss2<int>();
			mc3[0] = 100;
			mc3[1] = 200;
			Console.WriteLine(mc3[0]);
			Console.WriteLine(mc3[1]);
			Console.ReadKey();
		}
	}
        public class MyCalss
	{
		//private string[] _data;	//这样写不行
		private string[] _data=new string[5];
		public string this[int index]
		{
			get
			{
				return _data[index];
			}
			set
			{
				_data[index] = value;
			}
		}
	}
	//使用泛型可以使"算法"重用, 只是数据类型发生了改变。
	public class MyCalss2<T>
	{
		//private string[] _data;	//这样写不行
		private T[] _data = new T[5];
		public T this[int index]
		{
			get
			{
				return _data[index];
			}
			set
			{
				_data[index] = value;
			}
		}
	}
}

泛型约束

  • 使用where关键字进行类型约束
约束 说明
T:结构 类型参数必须是值类型,可以指定除Nullable 以外的任何值类型。
T:类 类型参数必须是引用类型,这一点也适用于任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的公共构造函数。当与其它约束一起使用时,new()约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类
T:<接口名称> 类型参数必须是指定的接口或者实现指定的接口,可以指定多个接口约束,约束接口也可以是泛型的
T:U 为T提供的类型参数必须是为U提供的参数或派生自为U提供的参数,这你为现类型约束
public class MyClass<T,K,V,W,X,Y,Z>
    where T:struct  //约束T必须是值类型
    where K:class    //约束K必须是引用类型
    where V:IComparable  
    where W:K    //要求W必须是K类型或者K类型的子类
    where X:class,new()  //X必须是引用类型并且具有一个无参的构造函数

MyClass<int,Stream,int,FileStream,Person,int,int> mm = new MyClass<int,Stream,int,FileStream,Person,int,int>();
mm[0] = 100;
Console.readkey();
  • 凡是具有GetEnumerator()这个方法的类型都能使用foreach来遍历。

其实通过foreach遍历数据,实际上是调用了一个"枚举器"来遍历数据,和foreach一点关系都没有,foreach只是一个语法上的简化而已。

通过foreach语句降低了枚举的复杂度

  • 可枚举类型(具有GetEnumerator()方法)、枚举器(具有IEnumerator接口中的成员的类)
  • IEnumerable实现该接口即为可枚举类型。(注: 只要有这个方法IEnumerator就可以了,无须实现这个接口IEnumerable, 即类不须继承此接口)
  • IEnumerator实现该接口为枚举器
  • foreach遍历类型推断为ojbect而不是实际类型,需要实现泛型版本的接口
  • foreach遍历修改数据???绝对不行。
  • //1. foreach循环是只读的。不能用来修改数据。
  • //2. foreach循环只进的,并且是一条一条循环的。
设计枚举器举例

//其实与Foreach无关,只是与枚举有关
Persion pp = new Persion();

IEnumerator p1 = pp.GetEnumerator();
while (p1.MoveNext())
{
	Console.WriteLine(p1.Current);
}

p1.Reset();
while (p1.MoveNext())
{
	Console.WriteLine(p1.Current);
}

Console.WriteLine("ok");





                        //调用
			PersonB pb = new PersonB();
			//IEnumerator ctor = pb.GetEnumerator();
			//while(ctor.MoveNext())
			//{
			//	Console.WriteLine(ctor.Current);
			//}

			foreach (var item in pb)
			{
				Console.WriteLine(item);
			}


//这个类型就是一个"枚举器"
//希望一个类型被"枚举", "遍历", 就要实现一个类, 该类就是一个"枚举器"
public class PersonB : IEnumerable
{
	private string[] _friends = new string[] { "张三", "李四", "王五", "赵六" };
	#region 
	/*
	public int Count
	{
		get
		{
			return _friends.Length;
		}
	}
	public string this[int index]
	{
		get
		{
			return _friends[index];
		}
		set
		{
			_friends[index] = value;
		}
	}
	*/
	#endregion

	public string Name { get; set; }
	public int Age { get; set; }

		
	public IEnumerator GetEnumerator()
	{
		return new MyIEnumerator(_friends);
	}
}


public class MyIEnumerator : IEnumerator
{
	//public object Current => throw new NotImplementedException();
	public object Current
	{
		get
		{
			if (cur>=0 && cur<_objArr.Length)
			{
				return _objArr[cur];
			}
			else
			{
				throw new IndexOutOfRangeException();
			}
		}
	}

	private string[] _objArr;
	public MyIEnumerator(string[] objArr)
	{
		this._objArr = objArr;
	}

	private int cur = -1;  //一般下标都是一开始指向了第一条的前一条
	public bool MoveNext()
	{
		if (cur + 1 < _objArr.Length)
		{
			cur++;
			return true;
		}
		return false;
	}

	public void Reset()
	{
		cur = -1;
	}
}

# Ant编程

进程

每一个应用都是一个进程

  • 可以用进程指定打开某个应用。
ProcessStartInfo psi = new ProcessStartInfo(@"c:\1.exe");
Process p = new Process();
p.StartInfo = psi;
p.Start();

线程

  • 线程分前台线程与后台线程
    • 前台线程:只有所有的前台线程都关闭才能完成程序关闭。
    • 后台线程:只要所有的前台线程结束,后台线程自动结束。
Thread th;
private void button1_Click(object sender, EventArgs e)
{
        //创建一个线程去执行这个耗时的方法
	th = new Thread(func1);
        //将线程设置为后台进程
        th.IsBackground = true;
        //标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程,由CPU决定。
        th.start();
        
}

void func1()
{
	for (int i = 0; i < 10000; i++)
	{
		Console.WriteLine(i);
	}
}

  • 在.Net下,是不允许跨线程的访问。
    //取消跨线程的访问
    Control.CheckForIllegalCrossThreadCalls = false;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
	//当你点击关闭窗体的时候,判断新线程是否为null
	if(th!=null)
	{
		//结束这个线程
		th.Abort();
	}
}
  • 线程被Abort之后, 不能再Start.
    Thread.Sleep(3000); //静态方法,可以使当前线程停止一段时间运行。
    Thread.CurrentThread 获得当前的线程引用。

当前窗体的宽度

this.Width //当窗体改变大小时不对
this.ClientSize.With //当窗体改变大小时也对
给按钮重新赋值一个新坐标:
btnUnLove.Location = new Point(r.Next(0,(this.ClientSize.Width - btnUnLove.Width) + 1), r.Next(0, (this.ClientSize.Height - btnUnLove.Height)+1);

====================================================

.Net 基础第二十二天

  • 委托
    委托就是指向一个方法 [作为方法的参数都会有一个类型,这个参数通常是既定的类型(string,int等等)或者自己定义的类型,而方法作为参数时的类型为委托)。]
    注: (1)委托变量要指向的函数在那里定义, 委托变量就在那里被赋值 (函数在那里编写, 则将编写好的函数赋给变量)
    (2)委托(委托变量)在那里被调用(运行), 委托变量就在那里被定义
    (3) 委托在那里声明都可以,因为不是在类的内部声明,不属于任何类。
点击查看代码
namespace 第22天委托
{
	//声明的委托必须与指向的方法具有同样的签名。
	public delegate void DelSayHello(string name);
	class Program
	{
		static void Main(string[] args)
		{
			//调用委托
			//首先创建委托对象
			//DelSayHello del = new DelSayHello(ChineseSayHello);
			//可以简写成这样
			DelSayHello del = ChineseSayHello;

			//del("张三");
			Test("张三", ChineseSayHello);

			ChineseSayHello("张三");
			Console.ReadKey();
		}


		public static void Test(string name, DelSayHello del)
		{
			del(name);
		}

		public static void ChineseSayHello(string name)
		{
			Console.WriteLine(@"你好,"+ name);
		}
		public static void JapaneseSayHello(string name)
		{
			Console.WriteLine(@"o ha yo," + name);
		}


	}
}
  • 匿名函数
点击查看代码
namespace 第22天匿名函数
{
	delegate string DelTest(string name);
	class Program
	{
		static void Main(string[] args)
		{
			//DelTest del = Test;
			//string s = del("张三");
			//Console.WriteLine(s);
			//Console.ReadKey();

			DelTest del = delegate (string name)
			{
				return name;
			};

			string s = del("张三");
			Console.WriteLine(s);
			Console.ReadKey();

		}

		//public static string Test(string name)
		//{
		//	return name;
		//}
	}
}
  • 泛型委托
点击查看代码
namespace 第22天泛型委托
{
	public delegate int DelCompart<T>(T t1, T t2);
	class Program
	{
		static void Main(string[] args)
		{
			//int[] nums = { 1, 2, 3, 4, 5, };
			//int max = GetMax<int>(nums, Compare1);
			//Console.WriteLine(max);
			//Console.ReadKey();

			//string[] names = { "2121","trejfewioj","47953ujidlvjda034tufqep"};
			//string max = GetMax<string>(names, Compare2);
			//Console.WriteLine(max);
			//Console.ReadKey();

			Person[] pers = { new Person { Age = 19 }, new Person { Age = 30 } };
			Person p =GetMax<Person>(pers, Compare3);
			Console.WriteLine(p.Age);
			Console.ReadKey();


		}

		public static T GetMax<T>(T[] nums, DelCompart<T> del)
		{
			T max = nums[0];
			for (int i = 0; i < nums.Length; i++)
			{
				//if (nums[i] > max)
				if (del(max,nums[i])<0)
				{
					max = nums[i];
				}
			}
			return max;
		}

		public static int Compare1(int n1, int n2)
		{
			return n1 - n2;
		}

		public static int Compare2(string n1, string n2)
		{
			return n1.Length - n2.Length;
		}

		public static int Compare3(Person p1, Person p2)
		{
			return p1.Age - p2.Age;
		}

	}

	public class Person
	{
		public int Age { get; set; }
	}
}

Lamda表达式

点击查看代码
namespace 第22天lamda表达式
{
	//public delegate void DelTest();
	public delegate string DelTest(string name);
	class Program
	{
		static void Main(string[] args)
		{
			//lamda表达式-->匿名函数-->函数
			//DelTest del = Test;
			//del();
			/*
			DelTest del = Test;
			string s = del("张三丰");
			Console.WriteLine(s);
			Console.ReadKey();
			*/

			/*
			//匿名函数
			DelTest del = delegate (string s1) { return s1; };
			string s = del("张三");
			Console.WriteLine(s);
			Console.ReadKey();
			*/

			//不明觉厉
			DelTest del = (string name) => { return name; };
			string s = del("张三");
			Console.WriteLine(s);
			Console.ReadKey();
		}


		//public static void Test()
		//{

		//}

		/*
		public static string Test(string name)
		{
			return name;
		}
		*/
	}
}
  • lamda表达式语法
点击查看代码
namespace 第22天lamda表达式语法
{
	public delegate void DelTest1();
	public delegate void DelTest2(string s);
	public delegate string DelTest3(string s);
	class Program
	{
		static void Main(string[] args)
		{
			//DelTest1 del = () => { };
			//DelTest2 del2 = (string s) => { };
			//DelTest2 del3 = (string s) => { return s; };

			List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

			list.RemoveAll(n => n> 2);
			//list.RemoveAll((n) => { return n > 2; }); //象这样写也可以
			for (int i = 0; i < list.Count; i++)
			{
				Console.WriteLine(list[i]);
			}
			Console.ReadKey();

		}
	}
}
- 多播委托
点击查看代码
namespace 第22天多播委托
{
	public delegate void DelTest();

	class Program
	{
		static void Main(string[] args)
		{
			DelTest del = Test1;
			//del();
			del += Test2;
			del += Test3;
			//del -= Test2;

			//多播委托的一个大缺陷
			del = Test4;
			del();
			//事件	-->为了解决这个大缺陷,于是就有了事件。
			Console.ReadKey();
		}


		public static void Test1()
		{
			Console.WriteLine("我是方法1");
		}

		public static void Test2()
		{
			Console.WriteLine("我是方法2");
		}

		public static void Test3()
		{
			Console.WriteLine("我是方法3");
		}

		public static void Test4()
		{
			Console.WriteLine("我是方法4");
		}
	}
}
  • 读一个文本文件的所有行:
    string[] lrcText = File.ReadAllLines(songPath,Encoding.Default);

  • ref 就是将值传递变成引用传递。不是将值类型转变为引用类型。

  1. 使用ref型参数时,传入的参数必须先被初始化。对out而言, 必须在方法中对其完成初始化。
  2. 使用ref和out时, 在方法的参数和执行方法时, 都要加ref或out关键字。以满足匹配。
  3. out适合用在需要return多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。

普通的参数传递都是 "值传递", 不管参数是值类型还是引用类型, 加上了ref才是"引用传递"

就是有4种情况: 值类型进行值传递, 引用类型进行值传递, 值类型进行引用传递, 引用类型进行引用传递。

out

匿名函数与lambda表达式的区别:

没有委托就没有匿名方法及lambda表达式。

Task 阻塞

耗时卡界面与不卡界面


async和await 使用

task抛异常


卡界面

private void button2_Click(object sender, EventArgs e)
{

	//Task task = new Task(() => { Console.WriteLine("111"); });

	Task<int> t1 = new Task<int>(() =>
	{
		Thread.Sleep(5000);
		return 1;
	});

	t1.Start();
        Console.WriteLine("111aaaa111");  //这一句会马上执行
	MessageBox.Show(t1.Result.ToString());
	//t1.ContinueWith(t =>
	//{
	//	MessageBox.Show(t1.Result.ToString());

	//},TaskScheduler.FromCurrentSynchronizationContext());
}

不卡界面

private void button2_Click(object sender, EventArgs e)
{

	//Task task = new Task(() => { Console.WriteLine("111"); });

	//Task<int> t1 = new Task<int>(() =>
	//{
	//	Thread.Sleep(5000);
	//	return 1;
	//});

	Task<int> t1 = Task<int>.Factory.StartNew(() =>
	{
		Thread.Sleep(5000);
		return 1;
	});

	//t1.Start();
	//MessageBox.Show(t1.Result.ToString());

	t1.ContinueWith(t =>
	{
		MessageBox.Show(t1.Result.ToString());

	}, TaskScheduler.FromCurrentSynchronizationContext());
}

posted on 2022-05-19 20:03  manber  阅读(232)  评论(0)    收藏  举报

导航