C#知多少,满满的知识点助你全面进阶

阅前提示

本篇文章主旨在于 总结在使用C#过程中一些模糊的概念并深入理解其本质。
并记录使用C#语言的一些技巧,提高我们的代码质量。
为了浏览舒适,涉及内容或示例代码较多的部分会提供跳转来阅读。
适合人群:对C# 有一定使用基础的人
阅读方式:浏览,是否已知并加深认识

获取更多:我的博客地址

C#Skills

1.可空修饰符 ?

// int i = null;   因为值类型不可以被赋值成Null这里会报错
int? i = null;   // 所以要使用 ? 修饰符

有关可空值类型的存储问题

Nullable

struct Nullable<T>
{
    bool hasValue;
    T value;
}

2.Box/UnBox

装箱:值类型转引用类型叫,过程:先在堆上分配新存储位置,将值类型数据复制到新存储位置中,将结果变为对新存储位置的引用。

反之,拆箱 ,过程:判断装箱的值类型是否可被转换成拆卸类型,赋值堆中的存储值。

int number = 10;
object obj;

obj = number;//boxing       IL_000...  box
number = (int)obj;//Unboxing    IL_000... unbox.any

装箱,拆箱会带来性能上的损耗,所以要避免不必要的装箱拆箱。

避免可变的值类型

​ 这会让你在装箱拆箱上分不清方向,所以处于规范,避免可变的值类型。


3.终结器

​ 存在不确定性,不知道何时被回收器调用

~ClassName(){}  被垃圾回收器主动调用

IDisposable

​ 结合using实现确定性终结

public void Dispose()
{
    ...Dispose     
    System.GC.SuppressFinalize(this);
}

SuppressFinalize:的作用是将该实例从(f-reachable)队列中主动移除。

f-reachable终结队列:这里存放着即将被执行终结方法的实例。只有终结方法被调用后,才能对这个对象进行垃圾回收,不然将一直存在引用。这个时间是不确定的,所以如果可以提前对此进行回收的话,就主动将其移除此队列。


4.System.Lazy< T >

推迟初始化

Lazy<classA> a = new Lazy<classA>(()=> new classA());
var b = a.value;//当第一次访问value时执行委托

5.协变与逆变

协变

观点:我们无法将List< string > 转换成List< object >,因为两者不具有协变性

原因:因为其都具备写入的功能,无法保证数据安全性。

安全协变性

原理:防止对 T 的输入

使用 out 修饰符

interface IReadOnlyA<out T>
{
    T First{get;}
}
协变的限制

1.只有泛型接口和泛型委托才可以协变;

2.“来源”和“目标”必须是引用类型。

3.接口和委托必须声明为支持协变,编译器必须验证协变类型只用于输出

逆变

与协变相反,限制条件类似。使用 in 修饰符,防止对 T 的输出

interface ICompareA<in T>
{
    bool Compare(T a,T b);
}

数组的协变

Fruits[10] f = new Apple[10];//这是被允许的

但对于可读可写的类型,这并不是安全的协变。

IEnumerable<Fruits> f = new Apple[10];//使用只读类型来确保安全

避免不安全的数组协变,考虑使用只读接口来安全转换


6.Func&Action

System.Func

代表有返回值的方法,最后一个类型参数总是委托的返回类型。

public delegate TResult Func<in T1,in T2,out TResult>(T1 a,T2 b) 

System.Action

代表返回 void 的方法

public delegate void Action<in T>(T a)

7.Lambda表达式树

persons.Where(person => person.Name == "AAAA");

对于这个Lambda表达式,在不同情况下具有两种含义。

IEnumerable

代表 委托参数,比如 Func< T,bool >

public IEnumerable<T> Where<T>(this Ienumerable<T> collection,Func<T,bool> predicate)

IQueryable

代表 表达式树参数,比如 Expression<Func<T,bool>>

public IQueryable<T> Where<T>(this ...,Expression<Func<T,bool>> predicate)

区别

委托:生成编译好的用于实现匿名函数的代码,这是实实在在的逻辑。

表达式树:生成的是对Lambda表达式进行描述的数据,这是再执行时用来分析Lambda,用分析得到的数据构造查询,并针对数据库执行。

简而言之:表达式树不是可执行的代码,而是数据,而委托是可执行的代码。


8.MulticastDelegate

多播委托

创建委托时,编译器自动使用System.MulticastDelegate类型。

添加方法时,MD类会创建委托类型的一个新实例,在新实例中为新增的方法存储对象引用和方法引用,并在委托实例列表中添加新委托实例。实际上,它维护着一个Delegate对象链表。


9.Event 安全地封装委托

相比于delegate的随意性,引入了新的关键字 event

表现

public event EventHandler<TArgs> TempEvent = delegate{};

优点

1.被修饰的委托无法被赋值操作

2.只能在包容类内部进行调用,外部只可以使用+=,-=

EventHandler

public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e) where TEventArgs:EventArgs;

这是一个为了编码规范而生的新的委托类型。包含两个参数,sender 包含对调用委托对象的引用,args 派生于EventArgs 用来包含事件的附加数据。


10.Foreach

本块内容涉及代码较多,单独提供跳转进行阅读。
C# IEnumerable Foreach 深入理解


11.Linq查询

Linq.IEnumerable 拓展了IEnumerable的功能

Enumerable< T> 的标准查询操作符

延迟执行

LINQ中大部分查询运算符都有一个非常重要的特性:延迟执行。这意味着,他们不是在查询创建的时候执行,而是在遍历的时候执行(换句话说,当enumerator的MoveNext方法被调用时)

List<int> a = new List<int>();
a.Add(3);
var q = a.Where(x=>(x%2) == 0);
q = q.Select(x=>x*10);  
a.Add(2);a.Add(4);
UnityEngine.Debug.Log("---foreach a----");
foreach(int v in a)
{Log(v);}// 3 2 4
Log("---foreach q----");
foreach(int v in q)
{Log(v);} // 20 40
Log("---foreach q again----");
foreach(int v in q)
{Log(v);} // 20 40

特别注意:如果想安全的操作集合,那再查询之后使用 ToXXX转换成想要的集合类型


12.BinarySearch

这种搜索采用的是二分法搜索算法,前提是要求元素已经排好序。若没有返回值,它返回一个负数,该值的按位取反(~)结果是“大于被查找元素的下一个元素”的索引,若没有则是元素的总数。方便插入新值

List<.string> list = new List<string>(){"p","a","c"};
list.Sort();
var search = list.BinarySearch("e");
if(search < 0)
    list.Insert(~search,"e");

13.特性

利用特性,可以指定与被修饰的构造有关的额外元数据。特性是将额外数据关联到属性(以及其他构造)的一种方式。[]

public class SimpleAttribute:Attribute{}

[Simple]
somethings...

AttributeUsageAttribute

1.ValidOn 限制特性能够修饰的东西 AttributeTargets

2.AllowMultiple 这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次

3.Inherited 表明当特性被放置在一个基类上时,它能否被派生类所继承

[AttributeUsage(AttributeTargets.Class,AllowMultiple = true)]
public class SimpleAttribute:Attribute{}

//表现
[Simple]
[Simple]
public class A{}

系统定义的特性

[Flags]
[Flags]
public enum FileAttributes
{
    ReadOnly = 1<<0;
    Hidden = 1<<1;
}
//用来改变ToString() 和 Parse()的行为
// 输出会变成 ReadOnly、Hidden 而不是数字
[("...")]
#define C_A
[Conditional("C_A")]
public void A(){}
//这个只有在 #define 存在时才能正常执行
//类似于 #if/#endif
[Obsolete]
[Obsolet("警告内容"),true]
public void A(){}
// 编译时会提示警告
//可填两个参数,内容 和 是否强制警告视为错误
[Serializable] [NonSerialized]

可序列化/不可序列化

[Serializable]
public class Skill13Data
{
    public string content;
    [NonSerialized]
    public string name;
    public Skill13Data(string content){
        name = "Skill13Dara";
        this.content = content;
    }
}

using(Stream stream = File.Open("Test.bin",FileMode.Create))
{
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.Serialize(stream,new Skill13Data("Hello world"));
}

using(Stream stream = File.Open("Test.bin",FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    var data = (Skill13Data)formatter.Deserialize(stream);
    Log(data.content); // Hello world
    Log(data.name); // Null 因为被标记了不可序列化
}

14.线程

基础

简单的线程实现

ThreadStart ts =  ThreadMethod;//该线程要执行的方法转换成委托型ThreadStart
Thread subT = new Thread(ts);//创建线程
subT.Start();//开始该线程
subT.Join();//让主线程等待该线程结束

线程池

 // 设置最大线程数
ThreadPool.SetMaxThreads(5,5);
//将任务添加进线程池,若线程已满则会排队等待
ThreadPool.QueueUserWorkItem(ThreadPoolMethod);
public void ThreadPoolMethod(object state){...}

.NET 4.0

.NET 4.0 后的TPL(Task Parallel Library) 任务进行库 提供了更加简便的异步任务编程模式。将异步工作抽象到Task对象中,通过提供代表工作单元的对象,TPL使我们能通过编程将小任务合并成大任务,从而建立起一个工作流。

任务是对象,其中封装了以异步方式执行的工作

不同于委托,委托是同步的,执行委托需要等待委托的结束

任务是异步的,启动任务,会立即返回调用者,任务在另一个线程上异步执行。

using System.Threading.Tasks;
// 两种创建方式
Task t1 = new Task(()=>{...});//Action
t1.Start();
Task<string> t2 = Task.Run(()=>{...return"Hellow";});//Func

//链接任务
Task task = Task
    .Run(()=>{Console.WriteLine("Hellow");})
    .ContinueWith((t)=>{Console.WriteLine("Wrold");});

//取消任务
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task task = Task.Run(()=>{},tokenSource.Token);
tokenSource.Cancel();// 实际上是设置tokenSource.Token.IsCancellationRequested该字段等待下一次检查,ture 则while循环退出。

N.关键字

sealed

​ 修饰类避免被继承,修饰方法避免被重写

using

​ 三种用法

using System; 引用命名空间
using Timer = System.Timers.Timer 为空间或类型取别名
using (DisposableSomething){...} 作为语句用于定义一定范围,在最后释放对象

第三点中,使用的对象必须实现IDisposable接口,保证该对象可被释放。可有多个参数但必须是同类型

using(Something){....}  相当于

try{...}

finally{ ((IDisposable)Something).Dispose()}

param

方法参数数组

public void F(param string[] st){}
F("Hello","World");

注意:参数必须是数组;不能与ref out 组合使用;

new

new AClass(); 运算符:创建对象调用构造函数
new public int a; 修饰符:显式隐藏从基类继承的成员
class AA<T> where T:new() 约束:泛型声明中约束可能用作类型参数的参数的类型

This

代表当前实例 this.a
串联构造函数 public A(string a):this(...)
拓展方法的第一个参数修饰符 public void F(this int a){...}

@

1.字符串开头声明,不需要对转义字符加(\)

2.@开头的字符串可以跨行,方便阅读

3.在关键字前面使用可以修饰成变量名

string path = @"c:\docs\a.txt" ;// c:\\docs\\a.txt
string t = @"hellow ""V"" " ;// "hellow \"V\" "  
string tt = @"  aaa
                bbb
                ccc
            ";
string @string = "string";

extern

修饰符用于声明在外部实现的方法。

托管代码时与 DllImport 属性一起使用;在这种情况下,该方法还必须声明为 static,如下面的示例所示:

[DllImport("avifil32.dll")]
private static extern void AVIFileInit();

partial

Partial是局部类型的标志。局部类型可以实现将一个类、结构或接口分成几个部分,分别放在在几个不同的.cs文件中(当然也可以放在同一个.cs文件中)。在程序进行编译之后,将会合并成一个完整的类。因此局部类型并没有看起来那么难以理解,使用partial只是让类变得更容易管理,实际使用时和普通的类一样。
如果一个类内容太多,或者存在一些自动生成的部分,可以使用该关键字,将类拆分成几个文件来管理。

public partial class AClass{...}

yield

yield关键字是一种语法糖,实际上还是通过实现IEnumberable、IEnumberable< T >、IEnumberator和IEnumberator< T >接口来满足迭代功能

详细解释请看上方 10.Foreach


链接

C# 官方文档

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345