C#高级特性--源动力

集合

集合(Collection)类是专门用于数据存储和检索的类。
这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的支持。
大多数集合类实现了相同的接口(IEnumerable)。
集合(Collection)类服务于不同的目的,如为元素动态分配内存,基于索引访问列表项等等,在 C# 中,Object 类是所有数据类型的基类。

image.png

集合--ArrayList

ArrayList:不定长度的,连续分配的。

示例代码:

//元素没有类型限制,任何元素都当成object处理,如果是值类型会有装箱操作
ArrayList arrayList = new ArrayList();
arrayList.Add("object");//可以放string
arrayList.Add(Enumerable.Range(1,100).ToArray());//可以放Array
Func<string, int> func = m => 1;
arrayList.Add(func);//可以放Delegate

示例代码:

//移除数据
arrayList.RemoveAt(0);//通过索引坐标移除
arrayList.Remove("object");//匹配第一个元素值,满足就移除

集合--List

List:泛型集合,内存上都是连续摆放的,不定长,保证类型安全,避免装箱拆箱(都是统一的类型)。

示例代码:

var list = new List<int>() { 1, 2, 3 };
list.Add(1);
list.AddRange(new List<int> { 4, 5, 6 });//批量添加
list.Any();//判断是否有数据
list.Clear();//清除所有数据
list.ForEach((m) => { });//foreach循环,参数=>viod委托
list.Skip(1).Take(2);//取索引为0之后的两条数据

集合--LinkedList

LinkedList:双向链表 元素不连续分配,每个元素都有记录前后节点。

image.png

示例代码:

//在头部和尾部都标识了上一个元素和下一个元素所处位置
LinkedList<int> list = new LinkedList<int>();
list.AddLast(1);
list.AddFirst(1);
list.AddBefore(list1, 0);//前面增加
list.AddAfter(list1, 0);//后面增加
list.Remove(1);
list.Remove(list1);//根据节点删除
list.RemoveFirst();
list.RemoveLast();
list.Clear();

链表不能通过元素索引访问。找元素只能遍历。增删比较快,增加或删除,只需把这个元素的前后两个元素指向的元素节点改一下。

集合--Queue

Queue队列就是先进先出。它并没有实现 IList,ICollection。所以它不能按索引访问元素,不能使用Add和Remove。

//示例: A不断写入任务,B不断获取任务执行 ,每次拿最近的一个任务
Queue<string> queue = new Queue<string>();
queue.Enqueue("object");//添加数据
queue.Enqueue("object1");
foreach (var item in queue)
{
    Console.WriteLine(item);
}
queue.Dequeue();//获取最先进入队列的元素,获得并移除
queue.Peek();//获取但不移除

Queue的常用方法和属性:

  • Enqueue():在队列的末端添加元素。
  • Dequeue():在队列的头部读取和删除一个元素,注意这里读取元素的同时也删除了这个元素。如果队列中不再有任何元素就抛出异常。
  • Peek():在队列的头读取一个元素,但是不删除它。
  • Count:返回队列中的元素个数。
  • TrimExcess():重新设置队列的容量,因为调用Dequeue方法读取删除元素后不会重新设置队列的容量。
  • Contains():确定某个元素是否在队列中。
  • CopyTo():把元素队列复制到一个已有的数组中。
  • ToArray():返回一个包含元素的新数组。

集合--Stack

Stack:栈也是链表, 先进后出,先产生的数据最后使用。

image.png

示例代码:

Stack<string> stack = new Stack<string>();
stack.Push("object");//添加数据
stack.Push("object1");
stack.Pop();//获取最后进入队列的元素  获得并移除
stack.Peek();//获取不移除

C#中(线程)栈的内存释放也是一样,先实例化的对象最后释放(在栈中声明的变量,最先声明的最后GC)。

集合--HashTable

Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key-value的键值对,其中key通常可用来快速查找,同时key是区分大小写,value用于存储对应于key的值。
Hashtable中keyvalue键值对均为object类型,所以Hashtable可以支持任何类型的key-value键值对。

示例代码:

Hashtable ht=new Hashtable(); //创建一个Hashtable实例
  ht.Add("E","e");//添加keyvalue键值对
  ht.Add("A","a");
  ht.Add("C","c");
  ht.Add("B","b");
  string s=(string)ht["A"];
  if(ht.Contains("E")) //判断哈希表是否包含特定键,其返回值为true或false
    Console.WriteLine("the E key exist");
  ht.Remove("C");//移除一个keyvalue键值对
  Console.WriteLine(ht["A"]);//此处输出a
  ht.Clear();//移除所有元素
  Console.WriteLine(ht["A"]); //此处将不会有任何输出

集合--Dictionary

Dictionary:相当于泛型版本的HashTable。在使用Dictionary前,你必须对它的键类型和值类型进行声明。
Dictionary的描述:
1、从一组键(Key)到一组值(Value)的映射,每一个添加项都是由一个值及其相关连的键组成
2、任何键都必须是唯一的
3、键不能为空引用null(VB中的Nothing),若值为引用类型,则可以为空值
4、Key和Value可以是任何类型(string,int,custom class 等)

1、创建及初始化
 Dictionary<int,string>myDictionary=newDictionary<int,string>();
 2、添加元素
myDictionary.Add(1,"C#");
myDictionary.Add(2,"C++");
myDictionary.Add(3,"ASP.NET");
myDictionary.Add(4,"MVC");

Hashtable和Dictionary区别:
1、Dictionary<K,V>在使用中是顺序存储的,而Hashtable由于使用的是哈希算法进行数据存储,是无序的。
2、Dictionary的key和value是泛型存储,Hashtable的key和value都是object。
3、Dictionary是泛型存储,不需要进行类型转换,Hashtable由于使用object,在存储或者读取值时都需要进行类型转换,所以比较耗时。

集合--非泛型集合

泛型集合类是在.NET2.0的时候出来的,也就是说在1.0的时候是没有这么方便的东西的。现在基本上我们已经不使用这些集合类了,除非在做一些和老代码保持兼容的工作的时候。

ArraryList被List<T>替代。
HashTable 被Dictionary<TKey,TValue>替代。
Queue 被Queue<T>替代。
SortedList 被SortedList<T>替代。
Stack 被Stack<T>替代。

泛型

泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许编写一个可以与任何数据类型一起工作的类或方法。可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。

这里是一个泛型类型Stack <T>,用于堆栈类型T的实例:

Stack<T>声明单个类型参数T:
public class Stack<T> { 
    int position; 
    T[] data = new T[100]; 
    public void Push (T obj) { 
       data[position++] = obj; 
    } 
    public T Pop() { 
       return data[--position]; 
    } 
} 
我们可以使用Stack <T>如下:
Stack<int> stack = new Stack<int>(); 
stack.Push(5); 
stack.Push(10); 
int x = stack.Pop(); // x is 10 
int y = stack.Pop(); // y is 5 

Stack< int> 用类型参数int填充类型参数T。
Stack<T> 是一个开放类型,而Stack <int>是一个闭合类型。

泛型--关联性泛型集合类

即键值对集合,允许通过Key来访问和维护集合。

1.Dictionary<TKey,TValue>
  Dictionary<TKey,TValue>可能是我们最常用的关联性集合了,它的访问,添加,删除数据所花费的时间是所有集合类里面最快的,因为它内部用了Hashtable作为存储结构,所以不管存储了多少键值对,查询/添加/删除所花费的时间都是一样的,它的时间复杂度是O(1)。
  Dictionary<TKey,TValue>优势是查找插入速度快,那么什么是它的劣势呢?因为采用Hashtable作为存储结构,就意味着里面的数据是无序排列的,所以想按一定的顺序去遍历Dictionary<TKey,TValue>里面的数据是要费一点工夫的。
  作为TKey的类型必须实现GetHashCode()和Equals() 或者提供一个IEqualityComparer,否则操作可能会出现问题。

2.SortedDictioanry<TKey,TValue>
  SortedDictionary<TKey,TValue>和Dictionary<TKey,TValue>大致上是类似的,但是在实现方式上有一点点区别。SortedDictionary<TKey,TValue>用二叉树作为存储结构的。并且按key的顺序排列。那么这样的话SortedDictionary<TKey,TValue>的TKey就必须要实现IComparable<TKey>。如果想要快速查询的同时又能很好的支持排序的话,那就使用SortedDictionary吧。

3.SortedList<TKey,TValue>
  SortedList<TKey,TValue>是另一个支持排序的关联性集合。但是不同的地方在于,SortedList实际是将数据存存储在数组中的。也就是说添加和移除操作都是线性的,时间复杂度是O(n),因为操作其中的元素可能导致所有的数据移动。但是因为在查找的时候利用了二分搜索,所以查找的性能会好一些,时间复杂度是O(log n)。所以推荐使用场景是这样地:如果你想要快速查找,又想集合按照key的顺序排列,最后这个集合的操作(添加和移除)比较少的话,就是SortedList了。

泛型--非关联性泛型集合类

非关联性集合就是不用key操作的一些集合类,通常可以用元素本身或者下标来操作。

List<T>
  泛型的List 类提供了不限制长度的集合类型,List在内部维护了一定长度的数组(默认初始长度是4)。所以如果知道我们将要用这个集合装多少个元素的话,可以在创建的时候指定初始值,这样就避免了重复的创建新数组和拷贝值。
  另外的话由于内部实质是一个数组,所以在List的未必添加数据是比较快的,但是如果在数据的头或者中间添加删除数据相对来说更低效一些因为会影响其它数据的重新排列。

LinkedList<T>
  LinkedList在内部维护了一个双向的链表,也就是说我们在LinkedList的任何位置添加或者删除数据其性能都是很快的。因为它不会导致其它元素的移动。一般情况下List已经够我们使用了,但是如果对这个集合在中间的添加删除操作非常频繁的话,就建议使用LinkedList。

HashSet<T>
  HashSet是一个无序的能够保持唯一性的集合。我们也可以把HashSet看作是Dictionary<TKey,TValue>,只不过TKey和TValue都指向同一个对象。HashSet非常适合在我们需要保持集合内元素唯一性但又不需要按顺序排列的时候。

SortedSet<T>
  SortedSet和HashSet,就像SortedDictionary和Dictionary一样,还记得这两个的区别么?SortedSet内部也是一个二叉树,用来支持按顺序的排列元素。

Stack<T>
  后进先出的队列
  不支持按下标访问

Queu<T>
  先进先出的队列
  不支持按下标访问

泛型--泛型类型参数

在泛型类型或方法定义中,类型参数是在其实例化泛型类型的一个变量时指定的特定类型的占位符。 泛型类( GenericList<T>)无法按原样使用,因为它不是真正的类型。 若要使用 GenericList<T>,必须通过指定尖括号内的类型参数来声明并实例化构造类型。 此特定类的类型参数可以是编译器可识别的任何类型。

示例代码:

GenericList<float> list1 = new GenericList<float>();
GenericList<ExampleClass> list2 = new GenericList<ExampleClass>();
GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();

在 GenericList<T> 的每个实例中,类中出现的每个 T 在运行时均会被替换为类型参数。

泛型--泛型约束

定义泛型类时,可以对调用端代码能够在实例化类时用于类型参数的几种类型施加限制。 如果调用端代码尝试使用约束所不允许的类型来实例化类,则会产生编译时错误。 这些限制称为约束。 通过使用 where 上下文关键字指定约束。
下表列出了六种类型的约束:

image.png

在 GenericList<T> 的每个实例中,类中出现的每个 T 在运行时均会被替换为类型参数。

where T:类(类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型)

class MyClass<U>
        where U : class///约束U参数必须为“引用类型”
 { 
}

 public void MyMetod<T>(T t)
       where T : class
 {          
 }

where T:<基类名>(类型参数必须是指定的基类或派生自指定的基类)

public class Employee{}

public class GenericList<T> 
                    where T : Employee

泛型--泛型方法

泛型方法在方法的签名中声明类型参数。
使用泛型方法,许多基本算法只能以通用方式实现。

这里是一个泛型的方法示例:

public void ShowT<T>(T t)
        {
            Console.WriteLine("ShowT print {0},ShowT Parament Type Is {1}", t, t.GetType());
        }
Console.WriteLine("*********泛型方法调用***************");
ShowT<int>(11);
ShowT<DateTime>(DateTime.Now);
ShowT<People>(new People { Id = 11, Name = "Tom" });
Console.ReadKey();
image.png

LINQ

LINQ代表语言集成查询(Language Integrated Query),是.Net框架的扩展,它允许我们用SQL查询数据库的方式来查询数据的集合,使用它,你可以从数据库、程序对象的集合以及XML文档中查询数据。
查询表达式由查询体后的from子句组成,其子句必须按一定的顺序出现,并且from子句和select子句这两部分是必须的。

image.png
image.png
image.png

参考:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/

image.png
image.png
image.png

LINQ提供了一系列的优势,其中最重要的是其强大的表达能力,使开发表达声明。一些LINQ的优点如下。

  • LINQ提供语法高亮,证明有助于找出在设计时的错误。
  • LINQ提供智能感知这意味着很容易写更精确的查询。
  • 写LINQ代码是相当快的,因此开发时间也被显著减少。
  • LINQ使得调试方便,因为它在C#语言的集成。
  • 两个表之间的关系看很容易使用LINQ由于其分层特征,这使得编写查询在更短的时间加入多个表。
  • LINQ允许一个单一的LINQ语法的使用,同时查询多个不同的数据源,这是主要是因为其统一的基础。
  • LINQ是可扩展的,这意味着有可能使用LINQ的知识来查询新的数据源类型。
  • LINQ提供了一个查询连接多个数据源,以及突破复杂问题转换为一组短的查询易于调试的工具。
  • LINQ提供易于改造转换一种数据类型到另一种,如SQL数据转换为XML数据。

委托

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。顾名思义,让别人帮你办件事。委托是C#实现回调函数的一种机制。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

image.png

这个实例演示了委托的用法。委托 printString 可用于引用带有一个字符串作为输入的方法,并不返回任何东西。
我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件

lambda表达式

Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数。

首先定义一个Citys集合,初始化有一些数据。然后调用LINQ的first方法,查询出来长度大于7的第一个结果,看到了吧,这里用的就是Lambda表达式,

image.png

如果我们自己写,还要写循环遍历集合,然后判断字符串长度是否大于7,起码要写四五行代码,而这里只要一行就够了,而且LINQ也要写很长。

image.png

这里用的是最简单的Lambda表达式,(input parameters) => expression的形式。

利用委托方法和lambda表达式将2013替换成2014,当然也可以做其他任何操作,是由我们传入的lambda表达式决定的

Lambda 的特点

  • Lambda 中包含输入参数的数量,必须与委托类型包含的参数数量一致。
  • Lambda 中的每个输入参数,必须都能够通过隐式转换为其对应的委托参数类型。
  • Lambda 中的返回值(如果有),必须能够隐式转换为委托的返回类型

反射

什么是反射
反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

反射(Reflection)有下列用途:
它允许在运行时查看特性(attribute)信息。
它允许审查集合中的各种类型,以及实例化这些类型。
它允许延迟绑定的方法和属(property)。
它允许在运行时创建新类型,然后使用这些类型执行一些任务。

反射用到的主要类:
System.Type 类:通过这个类可以访问任何给定数据类型的信息。
System.Reflection.Assembly类:它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。
System.Type类:对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。获取给定类型的Type引用有3种常用方式:

image.png

Type类的属性:
Name 数据类型名;
FullName 数据类型的完全限定名(包括命名空间名);
Namespace 定义数据类型的命名空间名;
IsAbstract 指示该类型是否是抽象类型;
IsArray 指示该类型是否是数组;
IsClass 指示该类型是否是类;
IsEnum 指示该类型是否是枚举;
IsInterface 指示该类型是否是接口;
IsPublic 指示该类型是否是公有的;
IsSealed 指示该类型是否是密封类;
IsValueType 指示该类型是否是值类型;

Type类的方法:
GetConstructors():返回ConstructorInfo类型,用于取得该类的构造函数的信息;
GetEvents():返回EventInfo类型,用于取得该类的事件的信息;
GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息;
GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口的信息;
GetMembers():返回MemberInfo类型,用于取得该类的所有成员的信息;
GetMethods():返回MethodInfo类型,用于取得该类的方法的信息;
GetProperties():返回PropertyInfo类型,用于取得该类的属性的信息可以调用这些成员,其方式是调用Type的InvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法;

image.png

优点:
1、反射提高了程序的灵活性和扩展性。
2、降低耦合性,提高自适应能力。
3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:
1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

事件

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。

image.png
image.png
image.png

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

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

推荐阅读更多精彩内容

  • C#集合相关知识 C#里面Collection下面ArrayList 1、动态数组 Array List:动态数组...
    学习中的小白阅读 186评论 0 0
  • C#集合 有两种主要的集合类型:泛型集合和非泛型集合。 泛型集合被添加在 .NET Framework 2.0 中...
    OctOcean阅读 835评论 0 3
  • LINQ(Language Integrated Query,语言集成查询)提供了类似于SQL的语法,能对集合进行...
    编程小世界阅读 273评论 0 0
  • 记录自己在学习c#遇到的知识点(容易忽略容易忘记得,或一些小技巧)[持续更新] 前言: 在大部分应用情况下,"效率...
    wwmin_阅读 1,146评论 0 51
  • C#集合提供了一系列丰富的数据结构,可以帮助你更好地管理、操作和组织数据。 1、灵活的List<T> List<T...
    浮萍已逝阅读 47评论 0 0