C#入门经典_注释笔记

checked关键字

用来检查赋值是否数据溢出,或者在VS中配置,项目-属性-生成-高级,检查上溢下溢。

1
2
3
4
checked     //unchecked 不检查
{
destination = (int)source;
}

委托

2种写法:

1
2
3
Calcul cal=new Calcul(Add);

Calcul cal=Add;

情况2自动初始化一个委托。

二维数组

1
2
int[,] ints = new int[10,4];
ints[2, 2] = 1;

ref out关键字

ref out 等也可以改变函数的签名(命名+参数),从而实现重载

断点

可以设置中断条件和击中次数。如,a>=100,次数=100。

析构函数

1
2
3
4
class MyClass{
~MyClass(){
}
}

~ 析构函数,垃圾回收器回收时自动调用,还将隐式调用基类的析构函数。

隐藏基类方法,在子类中定义时加

1
2
3
new public void Do(){
//...
}

类型比较

1
myObj.GetType()==typeof(MyObjClass)

对象类型的比较:

myObj is objBase,myObj是objBase的子类,实现类,同类都为true。

重载运算符,例如:+

需要在进行+运算的类内部定义。

  • 不能重载+=,-=。但如果重载它们对应的简单运算符,如+,则+=仍能像预期那样执行
  • 不能重载=,因为它有基本用途
  • 不能重载&&和||,但同1理,重载&和|就够了
  • <和>,<=和>=必须成对重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person
{
public int Age { get; set; }
public decimal Price { get; set; }
public string Name { get; set; }

public static Person operator +(Person p1, Person p2)
{
return new Person { Age = p1.Age + p2.Age };
}

public static bool operator >(Person left, Person right)
{
return left.Age > right.Age;
}

public static bool operator <(Person left, Person right)
{
return left.Price < right.Price;
}
}

IComparable接口

定义在要比较的对象的类中实现,类要实现IComparable接口。

1
2
3
4
5
6
public int CompareTo(object obj)
{
var person = obj as Person;
if (person == null) throw new ArgumentException("参数错误");
return Age - person.Age;
}

重写隐式(implicit),显式(explicit)转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person1
{
public int Age { get; set; }

public static implicit operator Person2(Person1 p1)
{
Person2 p = new Person2 { Age = p1.Age };
return p;
}
}

class Person2
{
public double Age { get; set; }

public static explicit operator Person1(Person2 p2)
{
Person1 p1 = new Person1();
checked { p1.Age = (int)p2.Age; }
return p1;
}
}

同样可作用于泛型集合。

父子类相互转换

1会报错,2不会报错。不能把父类实例转换成子类实例,只能把父类变量类型转换成子类变量类型。

1
2
3
4
5
6
7
//1
Person p = new Person();
Man m = (Man)p;

//2
Person p1 = new Man();
Man m1 = (Man) p1;

如果强转换成as 则都不会报错,1为null,2不为null。

??运算符

1
2
3
int? val1 = null;
int result = val1*5 ?? 10;
int? result2=val1*5 ?? 10;

??等式的结果可以是int或int?,等式会自动进行转换。

default关键字

如果T是引用类型,则输出null。

如果T是值类型,则输出默认值。e.g.int -> 0。

1
2
3
4
5
6
class MyClass<T>{
public MyClass()
{
Console.WriteLine(default(T));
}
}

约束类型

1
2
3
4
class MyClass<T1, T2> : MyClassBase 
where T1 : class,new () where T2 : T1
{
}
  • 约束类型:struct值类型,class引用类型,base-class任意基类,interface接口,new()有一个公共无参构造函数
  • 多个约束之间用,分隔
  • new()作约束必须是指定的最后一个
  • 约束必须出现在继承说明符的后面

协变和抗变

应用于泛型接口和委托,继承关系默认支持协变。

协变

1
2
3
List<string> liststr = new List<string>();
List<object> listobj = new List<object>();
listobj.AddRange(liststr); //参数为IEnumerable<object>

定义:

1
2
3
public class List<T> : IEnumerable<T>...

public interface IEnumerable<out T> : IEnumerable
  • 使用关键字out,则类型参数T只能作为方法的返回值
  • 泛型类型参数可以从一个派生类隐式转化为基类,让一个带有协变参数的泛型接口(或委托)可以接收类型更加精细化,具体化的泛型接口(或委托)作为参数,可以看成OO中多态的一个延伸

抗变(逆变)

1
2
3
4
5
6
7
8
9
10
class MyComparer : IComparer<object>
{
public int Compare(object x, object y)
{
throw new NotImplementedException();
}
}

IComparer<object> comobj = new MyComparer();
liststr.Sort(comobj); //参数为IComparer<string>

定义:

1
public interface IComparer<in T>
  • 使用关键字in,则类型参数T只能作为方法的参数
  • 泛型类型参数可以从一个基类隐式转化为派生类,让一个带有协变参数的泛型接口(或委托)可以接收粒度更粗的泛型接口或委托作为参数,这个过程实际上是参数类型更加精细化的过程

总结:

协变让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值);逆变让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确。

引发事件时,会依次调用程序列表中每个处理程序,只要它们满足指定的条件即可

委托的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
delegate int OperationDelegate(int left,int right);

void Function(OperationDelegate del)
{
int left,right;
//...
del(left,right);
}

void int OperationEx(int left,int right)
{
return left+right;
}
  • 用方法创建委托:void Function(new OperationDelegate(OperationEx));
  • 直接传递方法(默认创建委托):void Function(OperationEx);
  • 匿名方法:void Function(delegate(int left,int right){ return left+right; });
  • Lambda表达式:
1
2
3
4
5
6
void Function((left,right) => left+right);  
//参数类型不用指定,会通过上下文推断出类型

Function((left,right) => (left-right)*right);
//Lambda表达式本质还是委托
//用Func<string x,int y...>来简化一个委托的定义。代替delegate xx....

Lambda表达式的解释

可以用两种方式解释Lambda表达式。

第一,Lambda表达式是一个委托,可以把它表示为如下泛型类型:

Action - lambda表达式不带参,返回void;
Action<> - lambda表达式最多8个参数,返回void;
Func<> - lambda表达式最多8个参数,返回不是void;

e.g.Func lamb。前面的是参数,最后的bool是返回值。

第二,可以把Lambda表达式解释为表达式树:

并不能直接执行。LINQ架构的泛型类 Expression<>,可用于封装Lambda表达式,把它转换为相应的SQL脚本,以便在数据库中直接执行。

工作目录

1
2
3
4
//设置当前工作目录,并不是转移文件!
Directory.SetCurrentDirectory(Directory.GetCurrentDirectory()+"\\demo");
//当前工作目录字符串
Directory.GetCurrentDirectory();

FileStream

1
2
3
4
5
6
7
8
9
10
11
12
13
FileInfo file = new FileInfo("test.txt");
//创建一个只读的流
FileStream stream = file.OpenRead();
//创建一个只写的流
FileStream stream1 = File.OpenWrite("test.txt");
//将文件指针移动到文件的第8个字节,起始位置为第1个字节
stream.Seek(8, SeekOrigin.Begin);
//将指针从当前位置向前移动2个字节,则指向8+2=10个字节
stream.Seek(2, SeekOrigin.Begin);
//查找文件中倒数第5个字节
stream.Seek(-5, SeekOrigin.End);
//FileStream 操作字节或字节数组的流
//Stream(StreamReader或StreamWriter)操作的是字符

FileStream读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//读取本身cs文件 
byte[] byData = new byte[200];
char[] charData = new char[200];

try
{
FileStream stream = new FileStream("../../Program.cs", FileMode.Open);
stream.Seek(110, SeekOrigin.Begin);
stream.Read(byData, 2, 190);
}
catch (Exception e)
{
Console.WriteLine("AN IO Exception throw!");
Console.WriteLine(e.ToString());
Console.ReadKey();
throw;
}

Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0);
Console.WriteLine(charData);

FileStream写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
byte[] byData;
char[] charData;

try
{
FileStream file = new FileStream("test1.txt", FileMode.Create);
charData = "This is a charData array!".ToCharArray();
byData = new byte[charData.Length];
Encoder encoder = Encoding.UTF8.GetEncoder();
encoder.GetBytes(charData, 0, charData.Length, byData, 0, true);
file.Seek(0, SeekOrigin.Begin);
file.Write(byData, 0, byData.Length);
}
catch (Exception)
{
//...
throw;
}

StreamReader/Writer的权限

StreamReader/Writer 总是拥有对文件的读写权限,为了使用高级参数如,FileMode,FileAccess,可以在FileStream构造函数指定这些参数,然后通过FileStream创建StreamReader/Writer。

读取数据的选择

1,小文件,StreamReader.ReadToEnd();

2,大型文件,StreamReader.ReadLine(); 循环判断是否为空或者

1
2
3
4
5
6
7
foreach (var item in File.ReadLines("test.txt",Encoding.Default))
{
Console.WriteLine(item);
}

File.ReadLines 一次读取一行,迭代读取。返回IEnumerable<string>
File.ReadAllLines 打开一个文件,读取文件的所有行,然后关闭文件。返回string[]

压缩和解压缩类 输入读取类

System.IO.Compression

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void SaveCompressedFile(string fileName, string data)
{
FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write);
GZipStream zip = new GZipStream(file, CompressionMode.Compress);
StreamWriter sw = new StreamWriter(zip,Encoding.Default);
sw.Write(data);
sw.Close();
}

static string LoadCompressedFile(string fileName)
{
FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read);
GZipStream zip = new GZipStream(file, CompressionMode.Decompress);
StreamReader sr = new StreamReader(zip, Encoding.Default);
string data = sr.ReadToEnd();
sr.Close();
return data;
}

C#6.0 新特性

  1. 字符串插值(String Interpolation)

之前:

1
2
var Name = "Jack";
var results = "Hello" + Name; 或者 var results = string.Format("Hello {0}", Name);

Now:

1
var results = $"Hello {Name}";

之前:

1
2
Person p = new Person {FirstName = "Jack", LastName = "Wang", Age = 100};
var results = string.Format("First Name: {0} LastName: {1} Age: { 2} ", p.FirstName, p.LastName, p.Age);

Now:

1
var results = $"First Name:{p.FirstName} LastName:{p.LastName} Age:{p.Age}";

还可以插入代码 - 相当于小脚本或Razor 的@{ },不过只能作用于string。

1
Console.WriteLine($"Jack is saying { new Tools().SayHello() }");
  1. 空操作符 ( ?. )

之前:

1
2
3
4
if (user != null && user.Project != null && user.Project.Tasks != null && user.Project.Tasks.Count > 0)
{
Console.WriteLine(user.Project.Tasks.First().Name);
}

Now:

1
Console.WriteLine(user?.Project?.Tasks?.First()?.Name);

还可用于数组索引器:

1
2
3
User[] users = null;
Console.WriteLine(users?[1].Name); // 正常
Console.WriteLine(users[1]?.Name); // 报错

以下代码并不会报错,也不会有输出。减少了空异常,但是我们却需要小心使用,因为有的时候我们确实是需要抛出空异常。那么使用这个特性反而隐藏了Bug。

1
2
User user = null;
user?.SayHello();

C#6.0 新特性 2

  1. NameOf
1
2
3
Console.WriteLine(nameof(User.Name)); //  output: Name
Console.WriteLine(nameof(System.Linq)); // output: Linq
Console.WriteLine(nameof(List<User>)); // output: List

NameOf只会返回Member的字符串,如果前面有对象或者命名空间,NameOf只会返回 . 的最后一部分, 另外NameOf有很多情况是不支持的,比如方法,关键字,对象的实例以及字符串和表达式。

  1. 表达式方法体

一句话的方法体可以直接写成箭头函数,而不再需要大括号。

1
2
3
4
private static string SayHello() => "Hello World";
private static string JackSayHello() => $"Jack {SayHello()}";
Console.WriteLine(SayHello());
Console.WriteLine(JackSayHello());

序列化对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
class Product
{
public long Id;

public string Name;

public double Price;

[NonSerialized]
string Notes;
public Product(long id, string name, double price, string notes)
{
Id = id;
Name = name;
Price = price;
Notes = notes;
}
public override string ToString()
{
return $"{Id}{Name} (${Price:F2}) {Notes}";
}
}

//Main中
List<Product> list = new List<Product>()
{
new Product(1,"Pung",1000.0,"Good stuff."),
new Product(2,"Soup",25.0,"Tasty."),
new Product(4,"Hat Sauce",12.0,"One for the kids.")
};

Console.WriteLine("Products to save:");
list.ForEach(p => Console.WriteLine(p.ToString()));

IFormatter serializer = new BinaryFormatter();
FileStream file = new FileStream("Products.bin", FileMode.Create, FileAccess.Write);
serializer.Serialize(file, list);
file.Close();

FileStream fileLoad = new FileStream("Products.bin", FileMode.Open, FileAccess.Read);
List<Product> list2 = serializer.Deserialize(fileLoad) as List<Product>;
fileLoad.Close();

监控文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private FileSystemWatcher watcher;

public Form1()
{
watcher = new FileSystemWatcher();
watcher.Deleted += new FileSystemEventHandler(OnDelete);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
InitializeComponent();
}

public void OnRenamed(object source, RenamedEventArgs e)
{
StreamWriter sw = new StreamWriter(@"F:\Logs\log.txt", true);
sw.WriteLine($"File renamed from {e.OldName} to {e.FullPath}");
sw.Close();
}
public void OnDelete(object source, FileSystemEventArgs e) //...

private void btnWatch_Click(object sender, EventArgs e)
{
watcher.Path = Path.GetDirectoryName(txtLocation.Text);
watcher.Filter = Path.GetFileName(txtLocation.Text);
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size;
watcher.EnableRaisingEvents = true;
}

XPath查询xml语言

1
2
3
4
5
6
XmlDocument xml = new XmlDocument();
xml.Load("../../Demo.xml");
XmlElement e = xml.DocumentElement;
var node = e.SelectSingleNode("Book[Title='韩寒']");
Console.WriteLine(node.SelectSingleNode("Content").InnerText);
xml.Save("../../Demo.xml");

LINQ查询大数集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static int[] GetNumbers(int count)
{
Random ran = new Random(0);
int[] result = new int[count];
for (int i = 0; i < count; i++)
{
result[i] = ran.Next();
}
return result;
}

static void Main(string[] args)
{
var nums = GetNumbers(12345678);
var query = from n in nums
where n > 1000
select n;
Console.WriteLine(query.Count());
Console.WriteLine(query.Max());
Console.WriteLine(query.Min());
// 默认版本.Sum()返回的是int,数值太大导致溢出
Console.WriteLine(query.Sum(p=>(long)p));
Console.WriteLine(query.Average());
Console.ReadKey();
}

LINQ 2

单值查询:.Distinct()(只选取没有重复的元素)
任意符合:.Any()(任意元素符合条件返回true)
全部符合:.All()(全部元素符合条件返回true)

.First()和.FirstOrDefault()区别:

.First()当条件不满足时,抛出异常;
.FirstOrDefault()当条件不满足时,返回一个null;

LINQ 3

集运算符:

.Intersect():使用默认的相等比较器得出两个序列的交集
.Except():同上,得出差集
.Union():同上,得出并集

Join查询:

1
2
3
4
var orderIds = from n in list select n;
var customers=from n in list
join m in orderIds on n.Id equals m.Id //equals 关键词,必须
select n.Amount+m.Amount;

LINQ TO XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//从字符串转换
XDocument d1 = XDocument.Parse(@"
<customers ID=""A"" City=""NY"" Region=""North American"">
< order Item = ""Widget"" Price = ""100"" />
< order Item = ""Tire"" Price = ""200"" />
</ customers >
");

XDocument doc = new XDocument(
//内部有其他元素,则有闭合标签
new XElement("customers",
new XAttribute("ID", "A"),
new XAttribute("City", "NY"),
new XAttribute("Region", "North American"),
//内部没有其他元素,则自闭
new XElement("order",
new XAttribute("Item", "Widget"),
new XAttribute("Price", 100)
),
new XElement("order",
new XAttribute("Item", "Tire"),
new XAttribute("Price", 200)
)
)
);

string path = "../../demo.xml";
doc.Save(path);

XDocument d = XDocument.Load(path);
Console.WriteLine(d);

var query = from n in doc.Descendants("order") select n.Name;
//.Elements()返回第一级子元素
//.Descendants()返回所有子元素
// 重载形式可以指定元素名
//.Attributes()返回当前元素所有特性
欢迎打赏