S2 第二本书 深入.NET平台和C#编程 总结 by天命

时间:2021-09-04 15:16:00

第一章 深入.NET框架

.NET框架 包含 CLR公共语言运行时 FCL框架类库

CLR(Common Language Runtime)公共语言运行时
CLS(Common Language Specific)公共语言规范
CTS(Common Type System)公共类型系统

FCL(Framework Class Library)框架类库
基本框架类(线程
ADO.NET XML类 文件IO类......
Web窗体 MVC WinForms

ADO.NET
SqlConnection SqlCommand SqlDataReader SqlDataAdapter
DataSet

C#代码→C#编译器→Microsoft中间语言(MSIL)→CLR JIT编译器→平台专用代码
Java源代码→Java编译器→字节码文件→JVM(Java虚拟机)→平台专用代码

类库 一堆类的集合
使用泛型 System.Collections.Generic
对文件的基本操作 System.IO
对网络协议进行编程 System.Net
对数据库的访问 System.Data
开发Windows窗体 System.Windows.Forms
对GDI+基本图形的操作 System.Drawing
命名空间保存的是一批功能相似的类的集合

封装
自动属性会在底层生成匹配的似有字段
当有条件限制时,不可以使用自动属性替换常规属性

CLR的用途
CLR是所有.NET程序运行的换技能,是所有.NET程序都要使用的编程基础。所有.NET程序都受CLR监管和处理。这些操作包括进程内应用程序的加载,将IL语言转化为机器语言,异常管理,垃圾回收,加载程序集等。

class User
{
public string UID { get; set; }
public string PWD { get; set; }
public string Name { get; set; }
}
class Users
{
public static User[] u = new User[50];
public static string logUser;
}

使用实体类保存数据.

第二章 深入c#数据类型

结构体
一个业务中属性个数比较少且都是值类型,使用类可能引起内存浪费 为此提供了结构体Struct数据类型
(特殊的值类型)
public struct MyStruct
{
public int Age;
public void Show()
{
Console.WriteLine("MDZZ");
}
}
结构体是值类型
结构体可以有属性值和方法
用法和类没有区别
结构体可以不用new直接使用属性和方法
结构体中不能给字段赋初值,类可以
结构体中没有默认构造函数,也不能手动添加
所有的值类型都存储在栈上

见到new
调用构造 开辟空间 创建对象
构造 主要完成对成员变量初始化工作
1.方法名和类名相同
2.没有返回值(连void也不能有)
public class Dog
{
public string Name { get; set; }
public Dog(string Name)
{
this.Name = Name;
}
}
装箱和拆箱
将值类型转换为引用类型叫做装箱,反之叫做拆箱
装箱 int num = 0; Object o = num;
拆箱 int result = (int)o;

值传递和引用传递
public class Change
{
public void Do(int a, int b)
{
int temp = a;
a = b;
b = a;
}
}
在Main方法里的代码如下
Change c = new Change();
int a = 5;
int b = 10;
c.Do(a, b);
Console.WriteLine(a+" "+b);
Console.ReadKey();
运行结果: 5 10
值传递 开辟了新空间 所以Main方法中的a,b的值不会改变
ref int a,ref int b
ref a,ref b
Reference 引用
值传递无ref,分情况讨论
1.传递的数据类型是值类型,不会保留
2.若传递引用类型,永久保留
引用传递有ref,无论传递的数据类型是值类型或引用类型,都会保留

值类型和引用类型变量的区别
1.值类型分配在栈上,引用类型分配在托管堆上。
2.值类型继承自ValueType类,引用类型不会。
3.值类型变量包含实例数据,引用类型变量保存了真实内容的引用地址
4.值类型不由GC(垃圾回收机制)管理,自动释放,效率比引用类型高
5.值类型不能被继承,引用类型可以被继承
6.值类型不能为null值,自动初始化为0,;引用类型默认为null

结构是值类型,类是引用类型
结构不能继承也不能被继承
值没有默认构造
结构体没有析构函数,类有析构函数
结构体可以不使用new初始化,但是类必须用

第三章 使用集合组织相关数据

1.以后开发中都会使用集合作为数据容器
2.非泛型集合
单列 ArrayList 双列 HashTable
3.泛型集合
单列 List<T> 双列 Dictionary<K,V>

array 数组 Hashtable双列集合 collection收集
dictionary 字典 generic 一般的,类的

数组:内存中开辟的一连串空间
集合:将一堆数据类型相同的数据放入一个容器内

单列:ArrayList
Add(Object):可以添加任何数据类型
引发问题:遍历时除非将但各元素的类型写成Object
Remove(真实的元素本身Object)
--根据索引删除:RemoveAt(int Index)
--集合中元素删除后 索引会自动维护

双列:Hashtable
1.他的元素和Add进去的顺序可能不一致
2.没有下标

关于for增强 C#
foreach (Object item in list)
{
Console.WriteLine(item);
}
item是一个迭代变量,不可被改变
关于for增强 Java
for(String item : names){
System.out.println(item);
if(item.equals("2")){
item="5";
}
}
没有语法错误,但是无法改变,因为改变的是迭代变量item的值,而不是和数组相关的某个下标对应的值

1.使用集合需要引入命名空间System.Collections
2.如果集合中元素为0,没有开辟空间
3.默认如果集合中出现了第一个元素 集合的容量+4,之后每次超出上限 集合的容量翻倍

泛型:就是为了约束ArrayList中元素类型,而制定的一个新的集合类型,该类型只能加入同一类型的多个元素,标识符<T>可以看成是一个占位符,泛型是将运行时错误提前到了编译时。

List<string> list = new List<string>();
list.Add("天命");
list.Add("斯沃");
list.Add("小高"); foreach (string item in list)
{
Console.WriteLine(item);
} //ArrayList上的增删改查都适用 //删除集合中的所有元素
list.Clear();
//查找有没有元素 list.Count //集合中的元素个数
list.Capacity //集合的容量
//Contains()判定某个元素是否在集合中存在
foreach (KeyValuePair<string,Student> item in dic)
{
Console.WriteLine(item.Key);
Console.WriteLine(item.Value.Age);
}
Console.WriteLine("---------------------------------"); foreach (string key in dic.Keys)
{
Console.WriteLine(key);
Console.WriteLine(dic[key].Age);
}
Console.WriteLine("---------------------------------"); foreach (Student stu in dic.Values)
{
Console.WriteLine(stu.Name);
Console.WriteLine(stu.Age);
} Console.ReadKey();

  以上是双列集合Dictionary的三种遍历方式

通过Key遍历 Value遍历 组合遍历

List<T>
List<string> list = new List<string>();
与数组的区别
动态扩容
移除集合中元素时,索引自动维护

Dictionary<K,V>
Dictionary<string,Student> dic = new Dictionary<string,Student>();

第四章 深入类的方法

构造函数
方法名称与类名相同,没有返回值类型,void都没有
用作给类的对象初始化
一个类中可以有多个构造
如果手动添加一个构造,系统不会自动生成无参构造

方法重载
在同一个类里 方法名称相同
方法参数列表不同(个数 类型 顺序)

//字段
public string Name { get; set; }
public int Age { get; set; }
//枚举性别
public enum Gender
{
male=,female=
}
public Gender PersonGender { get; set; } //无参构造
public Person()
{ } 带参构造
public Person(string name, int age, Gender gender)
{
this.Name = name;
this.Age = age;
class Add
{
//重载方法Add 传进两个int参数 返回相加的值
public int Sum(int a,int b)
{
Console.Write(a + "+" + b + "=");
return a + b;
} //传进三个double参数 返回相加的值
public double Sum(double a, double b, double c)
{
Console.Write(a + "+" + b + "+" + c + "=");
return a + b + c;
} //三个方法的参数列表不同 在同一个类里 方法名相同
//构成了方法重载
public string Sum(string a, string b)
{
Console.Write(a + "+" + b + "=");
return a + b;
}
}

this.PersonGender = gender;
}

啊上面格式弄错了 代码里插入了一个代码 有点鬼畜 但是不改了

接着继续下一章

第六章 初识继承和多态

继承:一个类可以使用另一个类的属性和方法(成为该类的成员)

子类继承父类使用的符号是冒号

子类不能继承父类的构造方法

子类不能访问父类的私有字段和方法

在调用子类构造的时候,没有使用base关键字调用父类的指定构造,那么默认调用父类的无参构造。

//定义一个Person类

class Person

    {

        public string Name { get; set; }

        public Gender Sex { get; set; }

        public void SayHello()

        {

        }

        public Person()

        {

        }

        public Person(string name, Gender sex)

        {

            this.Name = name;

            this.Sex = sex;

        }

//定义一个Chinese类 继承自Person

 class Chinese:Person

    {

        public int Age { get; set; }

        public Chinese()

        {

        }

       public Chinese(string name, int age, Gender sex)

        {

            this.Age = age;

            this.Name = name;

            this.Sex = sex;

        }

}

定义枚举 性别 Male,Female

public enum Gender

    {

        Female=,Male=

}

Chinese cn=new Chinese(“”,18,Gender.Male)

指向子类构造→不进入子类构造赋值→进入父类的构造体→回到子类构造→执行子类构造题,子类构造体执行完毕→跳转到Main→在内存中构建出Chinese

如果new一个子类对象,那么有如下执行流程

1.定位到子类对应构造函数,没有执行子类方法

2.转向父类的无参构造函数,执行父类构造方法体

3.专项子类方法体继续进行

(使用base关键字执行指定的父类构造函数)

Base关键字

base.父类成员 base.父类方法

Base关键字可以调用父类成员 方法 和构造

public Chinese(string name, int age, Gender sex):base(name,sex)

{

this.Age = age;

}

base(参1,参2)的顺序根据父类构造参数的参数顺序保持一致,但是变量名是和子类构造的参数变量名一致

public Person(Gender sex, string name)//修改父类参数顺序

子类调用参数的顺序需要变化

public Chinese(string name, int age, Gender sex):base(sex,name)

{

this.Age = age;

}

protected访问修饰符

访问修饰符private 私有的 只能在当前类使用

protected只能在该类和与其有继承关系的子类中使用

public可以在任何地方使用

继承的两大特性

单根性 一个类只能有一个直接父类

传递性 A是B的父类 B是C的父类 C仍然可以继承A的属性和方法

多态:不同的对象对于同一个操作做出的响应不同

多态目的是为了统一调用

public class Parent

{

public virtual void Eat(){}

}

public class Children:Parent

{

public override void eat{}

}

第七章 深入理解多态

第七章 深入理解多态

abstract 抽象的substitution 代替

principle 原则fly 飞speed 速度

job 职业code 代码tool 工具

多态三种实现方案

1.虚方法

2.抽象方法

3.接口实现多态

《大话设计模式》 《设计模式之禅》

里氏替换原则

在一个软件系统中,如果子类替换父类出现的位置,而对整个软件系统功能没有影响,就是里氏替换。

1.单一职责原则(Single-Resposibility Principle)

其核心思想为:一个类,最好只做一件事,只有一个引起它的变化。通常意义下的单一职责,就是指只有一种单一功能,不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。

2.开放封闭原则(Open-Closed principle)

其核心思想是:软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。

3.里氏替换原则(Liskov-Substituion Principle)

其核心思想是:Liskov替换原则,主要着眼于对抽象和多态建立在继承的基础上,因此只有遵循了Liskov替换原则,才能保证继承复用是可靠地。实现的方法是面向接口编程:将公共部分抽象为基类接口或抽象类,通过Extract Abstract Class,在子类中通过覆写父类的方法实现新的方式支持同样的职责。

Liskov替换原则是关于继承机制的设计原则,违反了Liskov替换原则就必然导致违反开放封闭原则。

Liskov替换原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余,避免运行期的类型判别。

4.依赖倒置原则(Dependecy-Inversion Principle)

其核心思想是: 抽象的稳定性决定了系统的稳定性,因为抽象是不变的,依赖于抽象是面向对象设计的精髓,也是依赖倒置原则的核心。

依赖于抽象是一个通用的原则,而某些时候依赖于细节则是在所难免的,必须权衡在抽象和具体之间的取舍,方法不是一层不变的。依赖于抽象,就是对接口编程,不要对实现编程。

5.接口隔离原则(Interface-Segregation Principle)

其核心思想是:接口应该是内聚的,应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,这是一种接口污染。

接口有效地将细节和抽象隔离,体现了对抽象编程的一切好处,接口隔离强调接口的单一性。而胖接口存在明显的弊端,会导致实现的类型必须完全实现接口的所有方法、属性等;而某些时候,实现类型并非需要所有的接口定义,在设计上这是“浪费”,而且在实施上这会带来潜在的问题,对胖接口的修改将导致一连串的客户端程序需要修改,有时候这是一种灾难。在这种情况下,将胖接口分解为多个特点的定制化方法,使得客户端仅仅依赖于它们的实际调用的方法,从而解除了客户端不会依赖于它们不用的方法。

分离的手段主要有以下两种:1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销。2、多重继承分离,通过接口多继承来实现客户的需求,这种方式是较好的。

6.迪米特法则(最少知道原则)

定义:一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块。扩展会相对容易。这是对软件实体之间通信的限制。它要求限制软件实体之间通信的宽度和深度。

原则是尽量使用合成/聚合的方式,而不是使用继承。

7.合成/聚合复用原则( Composite/Aggregate Reuse Principle 或 CARP )

定义:在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用这些对象的目的。

应首先使用合成/聚合,合成/聚合则使系统灵活,其次才考虑继承,达到复用的目的。而使用继承时,要严格遵循里氏代换原则。有效地使用继承会有助于对问题的理解,降低复杂度,而滥用继承会增加系统构建、维护时的难度及系统的复杂度。

密封类sealed 密封类不能有子类

抽象类和抽象方法

1.一个类用abstract修饰 就是抽象类

2.抽象类不能实例化(不能new)

3.抽象方法不能有方法体,甚至连{}都不能有

4.抽象方法只能存在于抽象类中

5.抽象方法存在的目的就是为了约束子类中方法存在的形式(参数列表和返回值类型)

6.抽象类不能是静态类或者密封类

7.抽象类中的所有抽象方法都必须被子类重写,除非子类本身也是一个抽象类(虚方法可以选择性被子类重写,抽象方法强制子类必须重写)

8.一件重写所有抽象方法的快捷键: ctrl+.  (无输入法)

或者Shift+Alt+F10 (任何输入法)

namespace 抽象类和抽象方法
{
public abstract class Traffic
{
public string Name { get; set; } public abstract void Run(); public void Show()
{
Console.WriteLine("受");
} public virtual void Show2(){} public Traffic() { }
public Traffic(string name)
{
this.Name = name;
}
}
}

在子类中重写 实现抽象方法

public override void Run()
{
Console.WriteLine(Name + "带着小姨子跑啦");
} public override void Show2()
{
Console.WriteLine("子类" + Name);
} public Car(string name)
: base(name)
{ }

第八章 可扩展语言MXL

XmlDocument 整个文档对象

Load("url")

DocumentElement 属性,获取根节点 返回值:XmlElement 继承自XmlLinkedNode 继承自XmlNode

一般开发可以使用XmlNode

Xml文件

区分大小写

有开始就必须有闭合(除非自闭合)

只有一个根节点

xml侧重于数据的存储

html侧重于数据的展示

在TreeView中,每个节点都对应一个TreeNode类型

xx.Text 当前节点对应文本

部分类 partial 被partial修饰的类被劈成了>=N的部分

但是底层编译器在编译时会合成一个类

static void Main(string[] args)
{
//构建XmlDocument对象
XmlDocument doc = new XmlDocument();
//load方法加载
doc.Load("Notes.xml");
//获取根节点 XmlNode root = doc.DocumentElement;
//循环遍历节点下的各节点 获取其中的内容
foreach (XmlNode item in root.ChildNodes)
{
foreach (XmlNode child in item.ChildNodes)
{
Console.WriteLine(child.InnerText);
}
}
Console.WriteLine("----------------------------------"); foreach (XmlNode item in root.ChildNodes)
{
Console.WriteLine(item.Attributes["id"].Value);
Console.WriteLine(item["type"].InnerText);
Console.WriteLine(item["location"].InnerText);
}
Console.ReadKey();
}

private void button2_Click(object sender, EventArgs e)

{

//1.内存中构建出一个TreeNode

TreeNode tn = new TreeNode(tb.Text);

//2.将游离的TreeNode绑定到tvList 成为根节点

tvList.Nodes.Add(tn);

}

private void button1_Click(object sender, EventArgs e)

{

TreeNode tn = new TreeNode(tb.Text);

TreeNode parent = tvList.SelectedNode;

parent.Nodes.Add(tn);

}

private void button3_Click(object sender, EventArgs e)

{

tvList.SelectedNode.Remove();

}

使用TreeView动态添加和删除节点 如上

第九章 文件操作

导入命名空间 using System.IO

向一个txt文件中写入数据

//创建文件流

FileStream fs = new FileStream("new.txt", FileMode.Create);

//创建写入器

StreamWriter sw = new StreamWriter(fs);

//将录入的内容写入文件

sw.Write(textBox1.Text);

//关闭写入器

sw.Close();

//关闭文件流

fs.Close();

文件流

FileStream 文件流对象 = new FileStream(string filePath, FileMode fileMode)

filePath指定需要操作的文件

fileMode指定打开文件的模式

Create 用指定的名称新建一个文件,如果文件存在 则改写旧文件

CreateNew 新建一个文件,如果已经存在会发生异常

Open 打开一个文件 指定的文件必须存在

OpenOrCreate 打开一个文件 如果不存在,创建这个文件并打开

Append 打开现有文件并在后面追加内容

关闭文件流 写入结束后要关闭文件流.Close()

文件读写器

StreamWriter写入器 传入FileStream对象

StreamWriter.Write()写入流

StreamWriter.WriteLine()写入一行

StreamWriter.Close()关闭写入器

!先关闭写入器对象 再关闭文件流对象

StreamReader读取器

StreamReader.ReadLine()读取一行 返回字符串

StreamReader.ReadToEnd()从当前位置读到末位,返回字符串

StreamReader.Close()关闭读取器

FileStream stream = new FileStream("1.txt", FileMode.Open);

            //将低级的stream包装成高级的流

StreamReader reader = new StreamReader(stream,Encoding.GetEncoding("gb2312"));

string b = reader.ReadLine();

MessageBox.Show(b);

乱码问题 Encoding.类型

文件和目录操作

File类和Directory类

File类的常用方法

Exists(string path)检查文件是否存在 bool

Copy(string path,string path1)从源目录复制目标文件到目标位置,如果没有源文件,新建一个 void

Move(string path,string path1)将指定文件移动到新路径 void

Delete(string path)删除指定文件 如果不存在,发生异常 void

Directory类的常用方法

Exists(string path) 检查指定文件夹是否在磁盘上存在 bool

Move(string path,string path1)将文件目录以及内容移动到新位置 void

Delete(string path)删除指定目录 如果recursive值为true,则删除子目录中的所有目录内容 void