C#高级编程笔记 (6至10章节)运算符/委托/字符/正则/集合
数学的复习,4^-2即是1/4/4的意思, 4^2是1*2*2的意思,而10^-2为0.01!
7.2运算符
符号 | 说明 | 例 | |
++ | 操作数加1 | int i=3; j=i++; | 运算后i的值为4,j的值为3 |
int i=3; j=++i; | 运算后i的值为4,j的值为4 | ||
-- | 操作数减1 | int i=3; j=i--; | 运算后i的值为2,j的值是3 |
int i=3, j=--; | 运算后i的值为2,j的值是2 | ||
&& | 执行逻辑运算,检查两个表达式是否为真 | int a=5;(a<10&&A>5) | False |
|| | 执行逻辑运算,检查两个表达式是否至少有一个为真 | int a=5;(a<10||A>5) | true |
& | 并集 AND | ||
| | 或集 OR | ||
typeof | 表示某种数据类型,返回类型 | typeof(string) | 返回字符类型 |
?: | 简化的IF语句 | 表达式?语句1:语句2 | 表达式为真时,执行语句1,否语句2 |
checked运算符可以检查类型强制转换是否安全 如果不如全会抛出一个溢出异常 checked((int)变量1)
checked在自定义类的强制转换时应有重载过程中检查,不应放在Main或调用时
int? 表示int可空类型,可以表示NULL 如 int? a = null; uint 表示无符号的int类型
字符串转换为数值int i = int.parse("100")
7.4.1比较引用类型的相等性(相关说明)
== 在比较值类型的时候,会自动转换并比较数值,如果值一样输出True;
== 在比较引用类型时,一般情况下比较的这是引用类型的引用是否相等;
Equals 与 ==相同(其中不同的地方是不会自动转换) 即: int i =5;double n =5 ,i.Equals(n)为False
ReferenceEquals 则不管是什么类型都只比较引用是否相同,因值类型需要装箱,部是返回false,所以值类型调用这个方法没什么意义!
7.5运算符的重载
operator重载运算符 C#要求所有的运算符重载都声明为public与static,运算符左边的参数命名为lhs,运算符右边的参数命名为rhs
public static Vector operator + (vector lhs, vector rhs) { Vector result = new Vector(lhs); result.x += rhs.x; return result; }
并不是所有的运算符都可以重载,可以重载的运算符如下表
类别 | 运算符 | 限制 |
算术二元运算符 | +、*、/、-、%、 | 无 |
算术一元运算符 | +、-、++、-- | 无 |
按位二元运算符 | &、|、^、<<、>> | 无 |
按位一元运算符 | !、~、true、false | true与false运算符必须成对重载 |
比较运算符 | ==、!=、>=、<、<=、> | 比较运算符必须成对重载 |
赋值运算符 | +=、-=、*=、/=、>>=、<<=、%=、&=、|=、^= | 不能显式地重载这些运算符,在重写单个运算符(如+、-、%等)时,它们会被隐式地重写 |
索引运算符 | [] | 不能直接重载索引运算符,第2章介绍的索引器成员类型允许在类和结构上支持索引运算符 |
数据类型强制转换运算符 | () | 不能直接重载类型强制转换运算符,用户定义的类型强制转换(本章的后面介绍)允许定义定制的类型强制转换行为 |
7.6用户定义的类型强制转换
public class Rational { private Int32 _inner_int = 0; public Rational() { } public Rational(Int32 num) { this._inner_int = num; } public Int32 ToInt32() { return this._inner_int; } // 隐式构造并返回一个合理的从一个Int32 public static implicit operator Rational(Int32 num) { return new Rational(num); } // 显式返回一个合理的Int32 public static explicit operator Int32(Rational r) { return r.ToInt32(); } public override string ToString() { //return base.ToString(); String s = String.Format("{0}", this._inner_int); return s; } } class Program { static void Main(string[] args) { Rational r1 = 10; Console.WriteLine(r1); //隐式转换 Int32 i =(Int32) r1; //Rational转换成 Int32时,指定了explicit(显式的),所以必须要指定转换类型Int32,如果将explicit换成implicit(隐式),int32i = r1 的代码将可以正常运行 Console.WriteLine(i); Console.ReadLine(); } }
8.2.3简单的委托示例
下面代码:实例化了一个委托数组DoubleOp(温馨提示:一旦定义了委托,基本就可以实例化它了,那么就可以像处理类那样,使用该委托了,所以这里把一些委托的实例放到数组中是可以的)。数组被初始化为由MathsOperations类实现的不同操作。然后遍历该数组,把每个操作应用到3个不同的值上,这恰好说明了委托的一种方式:把方法组合到一个数组中来使用,这样就实现了在循环中调用不同的方法了
using System; namespace Wrox.ProCSharp.Delegates { delegate double DoubleOp(double x);//定义 DoubleOp委托的数组,参数为X class Program { static void Main() { DoubleOp[] operations = { MathOperations.MultiplyByTwo, MathOperations.Square };//把MathOperations类的方法封装到委托的数组中! for (int i = 0; i < operations.Length; i++) { Console.WriteLine("Using operations[{0}]:", i); ProcessAndDisplayNumber(operations[i], 2.0); ProcessAndDisplayNumber(operations[i], 7.94); ProcessAndDisplayNumber(operations[i], 1.414); Console.WriteLine(); } Console.ReadLine(); } /// <summary> /// /// </summary> /// <param name="action">参数1:理解引入一个已选择好的委拖的方法,类型为委托类型</param> /// <param name="value">参数2:</param> static void ProcessAndDisplayNumber(DoubleOp action, double value) { double result = action(value);//调用委托,然后参数为value Console.WriteLine( "Value is {0}, result of operation is {1}", value, result); } } class MathOperations { public static double MultiplyByTwo(double value) { return value * 2; } public static double Square(double value) { return value * value; } } }
8.2.4 Action<T>和Func<T>
泛型Action<in T>委托表示一个void返回类型的方法, Func<in T1,out TResult>允许调用带返回类型的方法
using System; namespace Wrox.ProCSharp.Delegates { class Program { static void Main() { Func<double,double>[] operations = { MathOperations.MultiplyByTwo, MathOperations.Square };//把MathOperations类的方法封装到委托的数组中! for (int i = 0; i < operations.Length; i++) { Console.WriteLine("Using operations[{0}]:", i); ProcessAndDisplayNumber(operations[i], 2.0); ProcessAndDisplayNumber(operations[i], 7.94); ProcessAndDisplayNumber(operations[i], 1.414); Console.WriteLine(); } Console.ReadLine(); } static void ProcessAndDisplayNumber(Func<double, double> action, double value) { double result = action(value);//调用委托,然后参数为value Console.WriteLine( "Value is {0}, result of operation is {1}", value, result); } } class MathOperations { public static double MultiplyByTwo(double value) { return value * 2; } public static double Square(double value) { return value * value; } } }
8.2.5 BubbleSoter委托示例
class Program { static void Main() { Employee[] employees = { new Employee("Bugs Bunny", 20000), new Employee("Elmer Fudd", 10000), new Employee("Daffy Duck", 25000), new Employee("Wile Coyote", 1000000.38m), new Employee("Foghorn Leghorn", 23000), new Employee("RoadRunner", 50000) }; BubbleSorter.Sort(employees, Employee.CompareSalary);//传入参数1:数据,转入比较的方法 foreach (var employee in employees) { Console.WriteLine(employee); } Console.ReadLine(); } } class BubbleSorter { /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sortArray">数据1表示可以按索引单独访问的对象</param> /// <param name="comparison">方法,Func<T>委托,返回bool</param> static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison) { bool swapped = true; do { swapped = false; for (int i = 0; i < sortArray.Count - 1; i++) { if (comparison(sortArray[i + 1], sortArray[i])) { T temp = sortArray[i]; sortArray[i] = sortArray[i + 1]; sortArray[i + 1] = temp; swapped = true; } } } while (swapped); } } class Employee { public Employee(string name, decimal salary) { this.Name = name; this.Salary = salary; } public string Name { get; private set; } public decimal Salary { get; private set; } public override string ToString() { return string.Format("{0}, {1:C}", Name, Salary); } /// <summary> /// 比较二个数,类型参数1的工资小于e2为真,e2小于e1为假 /// </summary> /// <param name="e1"></param> /// <param name="e2"></param> /// <returns></returns> public static bool CompareSalary(Employee e1, Employee e2) { return e1.Salary < e2.Salary; } }
8.26多播委托
class Program { static void Main() { Action<double> operations = MathOperations.MultiplyByTwo;//封装一个方法放至委托中 operations += MathOperations.Square;//多播委托,还支持-=从委托中删除调用 ProcessAndDisplayNumber(operations, 2.0); ProcessAndDisplayNumber(operations, 7.94); ProcessAndDisplayNumber(operations, 1.414); Console.WriteLine(); Console.ReadLine(); } static void ProcessAndDisplayNumber(Action<double> action, double value) { Console.WriteLine(); Console.WriteLine("ProcessAndDisplayNumber called with value = {0}", value); action(value); } } class MathOperations { public static void MultiplyByTwo(double value) { double result = value * 2; Console.WriteLine("Multiplying by 2: {0} gives {1}", value, result); } public static void Square(double value) { double result = value * value; Console.WriteLine("Squaring: {0} gives {1}", value, result); } }
如果多播时如果一个委托方法出现异常,则整个迭代都会停止,可使用GetInvocationlist()按照调用顺序返回此多路广播委托的调用列表。
然后迭代delegates即可
Action d1 = One; d1 += Two; Delegate[] delegates = d1.GetInvocationList();
8.2.7 匿名方法
匿名方法不到跳到方法外部,也不能从外部跳入内部,匿名方法不能访问不安全的代码,
最好使用命名方法.C#3.0开始可以使用lambda代替匿名方法
static void Main() { string mid = ", 中间部分,"; Func<string, string> anonDel = delegate(string param) { param += mid; param += " 这是要添加的字符串."; return param; }; Console.WriteLine(anonDel("开始字符串")); Console.ReadLine();// 输出"开始字符串, 中间部分, 这是要添加的字符串. }
8.3 lambda表达式
lambda表达式的应用 上面的方法改为如下
"datagate(string 变量1)" 改为 "变量1=>"
static void Main() { string mid = ", 中间部分,"; Func<string, string> anonDel = param=> // delegate(string param) { param += mid; param += " 这是要添加的字符串."; return param; }; Console.WriteLine(anonDel("开始字符串")); Console.ReadLine();// 输出"开始字符串, 中间部分, 这是要添加的字符串. }
lambda 语句
func<double, double, double> twoParams = (x, y) =>x*y //方法内只有一条语句,会隐式添加return,多语句时要加花括号与return func<double, double, double> twoParams = (double x,double y) =>x*y//如不能匹配重载后的版本可以使用参数类型帮助找到匹配的委托
8.3.3闭包
通过lambda表达式可以访问lambda表达式块外部的变量-称为闭包
int i = 3; Func<int, int> test = j => j * i; Console.WriteLine(test(4));//输出12 i = 5; Console.WriteLine(test(4));//输出20 Console.ReadLine();
8.3.4 foreach闭包
var values = new list<int>() {10,20,30}; var funcs = new list<Func<int>>(); foreach(var val in values) { var v =val; //在C# 5.0不需要这样处理,但是在C# 4 和之前需要这样 不然全输出30; funcs.add(() => v); } foreach (var f in funcs) { Console.WritLine((f ())); }
8.4 事件
事件理解可解: 一个是触发事件的类,一个是订阅这个事件的类, main主程式中订阅事件,事件有单独一下类!下面代码可以看出对应的关系
阅读《C# 高级编程》,其中关于EventHandler的使用颇让人费解。
因为已经习惯了public event delegateName eventName;这种定义事件的方式,因此下面结合这种方式来解释一下书中示例的代码。
先来看看CarDealer类的定义:
//CarInfoEventArgs 这类可以理解为是传递的参数,直接影响CarDealer顾客类中订阅的输出参数!
public class CarInfoEventArgs : EventArgs //EventArge表示包含事件数据的类的基类,并提供要用于不包含事件数据的事件的值。这个基类更像是规范性的基类,http://www.cnblogs.com/porter/p/5895132.html { public CarInfoEventArgs(string car) { this.Car = car + " 测试"; } public string Car { get; private set; } } public class CarDealer { public event EventHandler<CarInfoEventArgs> NewCarInfo; //定义一个事件NewCarInfo,当提供数据时触发 public void NewCar(string car) //main过程中调用 { Console.WriteLine("汽车经销商, 新车 {0}", car); RaiseNewCarInfo(car);//调用RaiseNewCarInfo的方法 } protected virtual void RaiseNewCarInfo(string car) { EventHandler<CarInfoEventArgs> newCarInfo = NewCarInfo; if (newCarInfo != null) { NewCarInfo(this, new CarInfoEventArgs(car));//这里触发事件,参数1本身,事件的发送者,参数2提供事件的相关信息 } } }
这里实际上先定义了一个继承自EventArgs的类,这个类的作用是用来储存将用来传递给事件的参数,在本例中,就是string成员Car,表示新车的名字。
然后在CarDealer类中,定义了事件NewCarInfo,类型是EventHandler<CarInfoEventArgs>,这是EventHanlder的泛型。EventHandler<T>表示一个接受两个参数(object sender, TEventArgs e),返回类型为void的方法。其中,TEventArgs必须为派生自EventArgs类的类型。后一个参数就是储存事件所需要参数用的。
然后是两个方法,其中RaiseNewCarInfo方法就是执行事件的方法。该方法由NewCar方法调用,接受string参数。而NewCar方法开放给汽车销售商,每到一辆新车,就执行一次这个方法,执行需要把新车的名字传递给方法。
然后我们来看客户Consumer类:
public class Consumer { private string name; public Consumer(string name) { this.name = name; } /// <summary> /// 它的签名符合EventHandler<CarInfoEventArgs>类型:返回类型为void,接受一个object参数和一个CarInfoEventArgs参数。这就是Event事件可以接受的方法。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine("{0}: 汽车 {1} 是新的", name, e.Car); } }
客户类定义了字段name用来储存客户名,这个name会在NewCarIsHere方法中使用到。重点就是这个NewCarIsHere方法,观察可知,它的签名符合EventHandler<CarInfoEventArgs>类型:返回类型为void,接受一个object参数和一个CarInfoEventArgs参数。这就是Event事件可以接受的方法。
因此在Main类中执行的方法就很简单了
static void Main() { var dealer = new CarDealer(); //汽车经销商 var michael = new Consumer("顾客 迈克尔"); //消费者 dealer.NewCarInfo += michael.NewCarIsHere;//NewCarIsHere方法加入NewCarInfo事件 dealer.NewCar("法拉利"); var nick = new Consumer("顾客 塞巴斯蒂安"); //消费者 dealer.NewCarInfo += nick.NewCarIsHere; dealer.NewCar("Mercedes"); Console.WriteLine(); dealer.NewCarInfo -= michael.NewCarIsHere; //移除消费者的事件,这里不会触发 迈克尔NewCarIsHere方法 dealer.NewCar("Red Bull Racing"); Console.ReadLine(); }
http://blog.csdn.net/cyh1992899/article/details/52806690
8.4.3 弱事件(system.Windows)
弱事件管理器
使用弱事件需创建一个派生自WeakEventManager类的管理器类,这个类要实现单态模式!
弱事件管理类需在静态方法Addlistener()和RemoveListener(),用来添加和删除侦听器
还在重写基类的StartListening()和StopListening()方法
public class WeakCarInfoEventManager1 : WeakEventManager { /// <summary> /// 新汽车事件管理器1 /// </summary> public static WeakCarInfoEventManager1 CurrentManager { get { WeakCarInfoEventManager1 manager = GetCurrentManager(typeof(WeakCarInfoEventManager1)) as WeakCarInfoEventManager1; if (manager == null) { manager = new WeakCarInfoEventManager1(); SetCurrentManager(typeof(WeakCarInfoEventManager1), manager);//为指定的管理器类型设置当前管理器。 } return manager; } } public static void AddListener(object source, IWeakEventListener listener) //添加订阅(侦听器)事件 { CurrentManager.ProtectedAddListener(source, listener); } public static void RemoveListener(object source, IWeakEventListener listener)//移除订阅(侦听器)事件 { CurrentManager.ProtectedRemoveListener(source, listener); } void CarDealer_NewCarInfo(object sender, CarInfoEventArgs1 e)//参数1,参数2表示传递类 { DeliverEvent(sender, e); //将正在托管的事件传送到每个侦听器。 } protected override void StartListening(object source)//重写基类方法 { (source as CarDealer).NewCarInfo += CarDealer_NewCarInfo; //CarDealer为事件发送者类(事件源?) } protected override void StopListening(object source)//重写基类方法 { (source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo; } }
发布程序类
public class CarInfoEventArgs1 : EventArgs { public CarInfoEventArgs1(string car) { this.Car = car+" 从事件中传递的"; } public string Car { get; private set; } } public class CarDealer { public event EventHandler<CarInfoEventArgs1> NewCarInfo; public CarDealer() //实例化类用 { } public void NewCar(string car) { Console.WriteLine("CarDealer, new car {0}", car); if (NewCarInfo != null) { NewCarInfo(this, new CarInfoEventArgs1(car)); } } }
事件侦听器类
public class Consumer : IWeakEventListener // 接收事件的类提供事件侦听支持 { private string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs1 e) { Console.WriteLine("{0}: 汽车 {1} 是新的", name, e.Car); } bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) //接收集中事件管理器中的事件。 { NewCarIsHere(sender, e as CarInfoEventArgs1); return true; } } public class test : IWeakEventListener { public void test1(object sender, CarInfoEventArgs1 e)// { Console.WriteLine(" 测试类触发了 {0} ", e.Car); } bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) //接收集中事件管理器中的事件。 { test1(sender, e as CarInfoEventArgs1); return true; } }
main()
static void Main() { var dealer = new CarDealer(); var michael = new Consumer("小明"); var pray = new test(); WeakCarInfoEventManager1.AddListener(dealer, michael); WeakCarInfoEventManager1.AddListener(dealer, pray); dealer.NewCar("凯帝"); var sebastian = new Consumer("小花"); WeakCarInfoEventManager1.AddListener(dealer, sebastian); sebastian = null; dealer.NewCar("宝马"); WeakCarInfoEventManager1.RemoveListener(dealer, michael); dealer.NewCar("Red Bull Racing"); Console.ReadLine(); }
输出:
CarDealer, new car 凯帝 小明: 汽车 凯帝 从事件中传递的 是新的 测试类触发了 凯帝 从事件中传递的 CarDealer, new car 宝马 小明: 汽车 宝马 从事件中传递的 是新的 测试类触发了 宝马 从事件中传递的 小花: 汽车 宝马 从事件中传递的 是新的 CarDealer, new car Red Bull Racing 测试类触发了 Red Bull Racing 从事件中传递的 小花: 汽车 Red Bull Racing 从事件中传递的 是新的
相关弱事件请参考8.4.3节查看与读解!
泛弱事件管理器
,net 4.5 为弱事件提供了新的实现。泛型类 WeakEventManager<TEventSource, TEventArgs>,它派生自基类WeakEventManager,大大的简化了弱事件的处理
static void Main(string[] args) { var dealer = new CarDealer(); //事件源类 var michael = new Consumer("Michael");//侦听器类 WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", michael.NewCarIsHere); //WeakEventManager<事件源类,侦听器类>.AddHandler(事件源实例,"事件",侦听器类.侦听器事件方法) dealer.NewCar("Mercedes"); var sebastian = new Consumer("Sebastian"); WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", sebastian.NewCarIsHere); dealer.NewCar("Ferrari"); WeakEventManager<CarDealer, CarInfoEventArgs>.RemoveHandler(dealer, "NewCarInfo", michael.NewCarIsHere); dealer.NewCar("Red Bull Racing"); }
其它类与事件实例一样!事件源carDealer不同(省略了代码)
9.1 system.string类
方法 | 说明 | 格式 | 解释 |
compare | 比较字符串 | string.Compare(str1,str2); | 返回 str1对比str2大于1,相等0,小于-1 |
compare | 重载(忽略大小) | string.Compare(str1,str2,true) | 返回同上,参数3忽略大小,返回int |
concat | 合并字符串 | string.concat(str1,str2) | 返回合并多个字符串的合并为一个实例 |
copyto | 复制到字符数组 | str1.copy(2,char1,3,9) | 把str1的索引2开始的9个字符复制到char1的索引3覆盖 |
Format | 参数化处理 | string.Format("今天{0}","天气") | 返回"今天天气"字符串 |
IsNullOrEmpty | 判断是否为空或Null | sting.IsNullOrEmpty(str1) | 返回str1=""或str=Null 返回True 否则false |
Join | 将数组的元素以分隔符拼接成一个新的字符串 | string.Join(",",Arr1) | 返回Arr1[0],Arr1[1],…… 以参数1分隔的字符串 |
Contains | 判断字符串中存在某字符串 | str1.Contains(str2) | 返回存在返回true,不存在返回false |
StartsWith | 是否以字符串开头 | str1.StarWith(str2) | 返回如果str1开头是str2则true,否则false |
EndsWith | 是否以字符串结尾 | str1.EndsWith(str2) | 返回如果str1结尾是str2则true,否则false |
IndexOf | 等一次出现的位置 | str1.indexof(str2) | 返回str2等一次在str1的位置(索引值),如果不存在返回-1 |
lsatIndexOf | 最后一次出现的位置 | str1.indexof(str2) | 返回str2最后一次在str1的位置(索引值),如果不存在返回-1 |
Equals | 是否相等 | str1.Equals(str2) | 返回如果相等,则true,否则false |
Replace | 替换 | str1.Replace("中","上") | 返回把str1的"中"替换为"上"的新字符串 |
Insert | 插入 | str1.Insert(2,str2) | 返回 把str2插入到str1的位置2的新字符串 |
Remove | 删除 | str1.Remove(1,2) | 返回 删除位置1,长度2的新字符串 |
split | 返回以字符分割字符 | str1.split(',') str1.split(char1[]) |
返回 以','分割的字符串数组,或以char1[]分割 |
substring | 截取字符串 | str1.substring(3,2) | 返回 str1位置3,长2的新字符串 |
ToCharArray | 字符串转换为字符数组 | str1.ToCharArray() | 返回字符数组 |
Trim | 去两边的空格 | str1.trim() | 返回去掉str1两边的空格的新字符串 |
ToUpper | 转换大写 | str1.ToUpper() | 返回str1的大写字符串 |
ToLower | 转换小写 | str1.ToLower() | 返回str1的小写字符串 |
9.1.2 StringBuilder成员
方法 | 说明 | 格式 | 解释 |
Append | 追加字符串到结尾 | Bstr.Append("") | 追加字符串到结尾 |
AppendFormat | 追加格式字符串到结尾 | Bstr1.AppendFormat("{0:C}", Int); | 格式化结果到字符串中 |
Insert | 插入字符到指定索引 | Bstr1.Insert(6,str2) | 在Bstr1的位置6插入 |
Remove | 移除指定位置与数量的字符 | Bstr1.Remove(5,7) | 移除Bstr1位置5,7个字符 |
Replace | 替换指定的字符 | Bstr1.Replace("A","a") | 把A替换为a |
stringBuilder不能显式或隐式转换为string,只能使用tostring()方法
格式 | |||||
C | 数字 | 货币 | string.Format("{0:C}",0.2) | ¥0.20 | {0:C1} 为0.2 四舍五入 |
D | 整数 | 整数 | string.Format("{0:D3}",23) | 023 | 补全3位 |
E | 数字 | 科学计数 | string.Format("{0:E2}", 123456) | 1.23E+005 | 科学计数 |
F | 数字 | 小数点后的倍数 | string.Format("{0:F4}", 15) | 15.0000 | 精确位数 |
G | 数字 | 一般的数字 | string.Format("{0:E2}", 123456) | 1.23456E5 | - |
N | 数字 | string.Format("{0:N}", 14200) | 14,200.00 | {0:N3}为14,200.000 | |
P | 数字 | 百分比 | string.Format("{0:P}", 0.24583) | 24.58% | {0:P1}为24.6% |
X | 整数 | 十六进制 | string.Format("0x{0:X}", 60) | 0x3C | 十六进制60 |
0与# | 数字 | 0表示显示 | "{0:0000.00} 与{0:####.#)",194.039 | 0194.03与194 |
9.2正则表达式 system.text.RegularExpressions
Match mat = regex.match(str1,regstr,RegexOptions.IgnoreCase); //match表示只匹配第一个!
MatchCollection matchCol = Regex.Matches(str1,regstr,RegexOptions.IgnoreCase);//表示匹配所有字符,matchcol可迭量
RegexOptions选项说明 (多个选项可用"|"分隔,可省略)
Singleline | 正则的"."匹配\n | 默认不匹配\n |
IgnoreCase | 不区分大小 | 默认区分大小写 |
RightToLeft | 从右到左开始匹配(运行) | 默认从左到右 |
Multiline | 匹配多行 | 默认不匹配字符串的多行 |
ExplicitCapture | 指定只有显式命名或编号的组才进行捕获,即不想用"?:"时 | 这个当做贤学就好了- -(不懂) |
9.2.4匹配、组合各捕获
()表示组合
?:a 表示要匹配a便是不保存a 与a?的区别在于保存
10集合system.Collections
10.2集合接口和类型
接口 | 说明 |
IEnumerable<T> | 实现foreach语句,这个接口定义了GetEnumerator()的方法 ,返回一个实现了IEnumerator接口的枚举,如下:IDictionaryEnumerator enumerator = hastable.GetEnumerator(); while (enumerator.MoveNext()) { string key = enumerator.Key.ToString(); string value = enumerator.Value.ToString(); } |
ICollection<T> | 获取元素的数量、增加删除元素 相关连接 |
IList<T> | 表示可按照索引单独访问的对象的非泛型集合。派生自ICollection.add() |
list<int> intlist = new list<int>(10);//10个元素
intlist.Capacity = 20;//集合的容量
intlist.count;//元素的个数
intlist.TrimExcess();//清理容量,注元素超90%容量时不清理
add()
属性 | |
Capacity | 容量大小 |
count | 元素的个数 |
方法 | |
TrimExcess() | 清理容量,注元素超90%容量时不清理 |
Insert() | 插入元素 list.insert(3,new racer(6,"")) 插入到3位置 |
add() | 添加元素 list.add(new racer()) |
addRange() | 添加元素数组 list.addRange(new racer []{}) 把一另一个数组的元素添加入来 |
RemoveRange() | 删除元素,可删除多个RemoveRange(3,5)删除位置3的5个元素 |
Indexof() | 返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回-1。 相关连接 |
lastIndexOf() | 找到最后一个返回索引,没找到返回-1相关连接 |
findIndex() | 返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1相关连接 |
find() | 返回数组中满足提供的测试函数的第一个元素的值相关连接 |
findAll() | 返回数组中满足提供的测试函数的所有元素项相关连接 |
Sort() | 对元素进行排序 ,要实现IComparable接口,才能使用 连接Sort方法 |
ConvertALL() | 将当前 List<T> 中的元素转换为另一种类型,并返回包含已转换元素的列表 相关说明 |
AsReadOnly() | 只读集合 |
findindex()方法
// 学生类 class Stu { public string Name { get; set; } public int Age{get;set;} } //泛型列表 List<Stu> list = new List<Stu>(); list.Add(new Stu(){Name="zhang", Age=20}); list.Add(new Stu(){Name="Li", Age=20}); list.Add(new Stu(){Name="Wang", Age=20}); list.Add(new Stu(){Name="Liu", Age=20}); //FindIndex int index = list.FindIndex(s=>s.Name=="Liu"); //index = 3; index = list.FindIndex(s=>s.Name=="Li"); //index = 1;
10.4队列
10.6链表
LinkedList< T> . AddAfter()//在 LinkedList< T> 中的现有节点后添加新的节点或值。
LinkedList< T> . AddBefore()
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.IO; using System.Text.RegularExpressions; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { PriorityDocumentManager pdm = new PriorityDocumentManager(); pdm.AddDocument(new Document("one", "Sample", 8)); pdm.AddDocument(new Document("two", "Sample", 3)); pdm.AddDocument(new Document("three", "Sample", 4)); pdm.AddDocument(new Document("four", "Sample", 8)); pdm.AddDocument(new Document("five", "Sample", 1)); pdm.AddDocument(new Document("six", "Sample", 9)); pdm.AddDocument(new Document("seven", "Sample", 1)); pdm.AddDocument(new Document("eight", "Sample", 1)); pdm.DisplayAllNodes(); Console.ReadLine(); } } public class PriorityDocumentManager { /// <summary> /// 包括所有的文档的比重链接列表 /// </summary> private readonly LinkedList<Document> documentList; /// <summary> /// 包括最多10个元素的引用 /// </summary> private readonly List<LinkedListNode<Document>> priorityNodes; public PriorityDocumentManager() { documentList = new LinkedList<Document>(); priorityNodes = new List<LinkedListNode<Document>>(10); for (int i = 0; i < 10; i++) { priorityNodes.Add(new LinkedListNode<Document>(null)); } } public void AddDocument(Document d) { if (d == null) throw new ArgumentException("参数d不能为空。"); //如果值为空的时候,引发异常信息 AddDocumentToPriorityNode(d, d.Priority); //传替一个实例,和这个实例的优先级 } /// <summary> /// /// </summary> /// <param name="doc">Document实例</param> /// <param name="priority">优先级</param> private void AddDocumentToPriorityNode(Document doc, int priority) { if (priority < 0 || priority >= 10) throw new ArgumentException("优先级必须介于0到9之间。"); if (priorityNodes[priority].Value == null) { --priority; if (priority >= 0) { //检查下一个较低优先级 AddDocumentToPriorityNode(doc, priority); } else // 现在没有具有相同优先级或更低优先级的节点存在。 // 将新文档添加到结尾 { documentList.AddLast(doc); priorityNodes[doc.Priority] = documentList.Last; } return; } else // 存在一个优先级节点 { LinkedListNode<Document> prioNode = priorityNodes[priority]; if (priority == doc.Priority) // 存在相同优先级的优先级节点 { documentList.AddAfter(prioNode, doc);// prioNode修改节点信息,其后面的链为doc // 将优先级节点设置为具有相同优先级的最后一个文档 priorityNodes[doc.Priority] = prioNode.Next;// doc修改节点信息,其前面的链为prioNode } else // 只有较低优先级的优先级节点存在 { // 获取较低优先级的第一个节点。 LinkedListNode<Document> firstPrioNode = prioNode; while (firstPrioNode.Previous != null && firstPrioNode.Previous.Value.Priority == prioNode.Value.Priority) { firstPrioNode = prioNode.Previous; prioNode = firstPrioNode; } documentList.AddBefore(firstPrioNode, doc); // 将优先级节点设置为新值 priorityNodes[doc.Priority] = firstPrioNode.Previous; } } } public void DisplayAllNodes() { foreach (Document doc in documentList) { Console.WriteLine("priority: {0}, title {1}", doc.Priority, doc.Title); } } // 返回具有最高优先级的文档 // (这是链接列表中的第一个) public Document GetDocument() { Document doc = documentList.First.Value; documentList.RemoveFirst(); return doc; } } public class Document { public string Title { get; private set; } //标题 public string Content { get; private set; } //内容 public byte Priority { get; private set; } //优先 public Document(string title, string content, byte priority) { this.Title = title; this.Content = content; this.Priority = priority; } } }
10.7有序列表
SortedList< TKey, TValue> //有序列表会自动排序
add方法SortedList1.add("name","1");
foreach (KeyValuePair<string,string> item in strSortedList)//这里用VAR省事- - { Console.WriteLine(item.Key); } ///其它特性基本都是一样的 Console.ReadLine();
strSortedList.ContainsKey("name");//确定 SortedList<TKey, TValue> 是否包含特定的键。存在为trun,否则false
strSortedList.TryGetValue("环境变化", out str1 );//获取与指定参数1的值,传替到STR1,不存在时返回值为False
10.8字典
字典的关系: 键<->索引<->值 ,net提供了几个字典类,可以使用的最主要的类是Dictionary<TKey,TValue>
请查阅书籍中内容 网上内容
10.8.3lookup类
类新类Lookup<TKey, TElement>是.NET 3.5中新增的,它类似于Dictionary<TKey, TValue>,但把键映射到一个值集上。这个类在程序集System.Core中实现,用System.Linq命名空间定义。相关连接
10.8.4有序字典
SortedList<TKey,TValue>有序列表类使用的内存要比SortedDictionary<TKey,TValue>类少
SortedDictionary<TKey,TValue>有序字典类的元素插入和删除操作比较快
10.9集
HashSet<string> companyTeams = new HashSet<string>() { "Ferrari", "McLaren", "Mercedes" };
privateTeams.Add("Williams")//如果已存在返回false
hashset1.IsSubsetOf(hashset2);// hashset1是hashset2的子集时返回true, hashset2包括hashset1
hashset2.IsSupersetOf(hashset1); //hashset2是hashset1的超集时返回true
hashset1.Overlaps(hashset2);//hashset1与hashset2有交集的时候返回true
hashset1.UnionWith(hashset2);//修改hashset1的集合为hashset1与hashset2的合集
hashset1.ExcepWith(hashset2);//移除hashset1中hashset2的集合的元素
10.10可观性集合
static void Main() { var data = new ObservableCollection<string>(); data.CollectionChanged += Data_CollectionChanged; //添加到事件 data.Add("One"); data.Add("Two"); data.Insert(1, "Three"); data.Remove("One"); Console.ReadLine(); } static void Data_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Console.WriteLine("action: {0}", e.Action.ToString());//获取引发该事件的操作 if (e.OldItems != null)//获取受 Replace、Remove 或 Move 操作影响的各项的列表。 { Console.WriteLine("starting index for old item(s): {0}", e.OldStartingIndex);//获取更改发生处的索引。 Console.WriteLine("old item(s):"); foreach (var item in e.OldItems) { Console.WriteLine(item); } } if (e.NewItems != null)//获取更改中涉及的新项的列表 { Console.WriteLine("starting index for new item(s): {0}", e.NewStartingIndex);//获取更改发生处的索引 Console.WriteLine("new item(s): "); foreach (var item in e.NewItems) { Console.WriteLine(item); } } Console.WriteLine(); }
10.11.1 BitArray类
BitArray类是一个引用类型
var bits1 = new BitArray(8); //创建8位的数组 00000000 bits1.SetAll(true); //全部位数为1 11111111 bits1.Set(1, false); //设置位数1是0 11111101 bits1[5] = false; //设置位数5是0 11101101 bits1.Not(); //反转所有位数,即如果1则改为0,为0改为1 bits1.Or(bits2); //指定bits2元素对当前bits1元素执行按位“或”运算。101or100=101 满 bits2.And(bits1); //指定bits1元素对当前bits2元素执行按位“与”运算。101and100=100 二都是1时得1 bits1.Xor(bits2); //指定bits2元素对当前bits1元素执行按位“异或”运算。101xor100=111 不相同的时候为1
static void DisplayBits(BitArray bits) //用于控制台输出01 { foreach (bool bit in bits) { Console.Write(bit ? 1 : 0); } }
10.11.2 BitVector32
效率比BitArray,在知道位数时可用BitVector32代替!它是一个值类型
var bits1 = new BitVector32(); int bit1 = BitVector32.CreateMask(); //这是1的位 (可以理解为是标记位置)掩码 int bit2 = BitVector32.CreateMask(bit1); //这是在bit1位上的 int bit3 = BitVector32.CreateMask(bit2); //这是在bit2位上的 int bit4 = BitVector32.CreateMask(bit3); //这是在bit3位上的 int bit5 = BitVector32.CreateMask(bit4); //这是在bit4位上的 bits1[bit1] = true; //设置位的值 bits1[bit2] = false; bits1[bit3] = true; bits1[bit4] = true; //也可以这样设置 bits1[0x2] = true;设置倍十六进制的2的位数为1
BitVector32.Section sectionA = BitVector32.CreateSection(0xfff); //表示最底位的12位 Console.WriteLine("Section A: " + IntToBinaryString(bits2[sectionA], true)); static string IntToBinaryString(int bits, bool removeTrailingZero)//转为输出字符串 { var sb = new StringBuilder(32); for (int i = 0; i < 32; i++) { if ((bits & 0x80000000) != 0) //bits或运算最高位 { sb.Append("1"); //追加到结尾 } else { sb.Append("0"); } bits = bits << 1; //左移1位 } string s = sb.ToString(); if (removeTrailingZero) return s.TrimStart('0'); else return s; }
10.12不变的集合 System.Collectios.Immutable
List<Account> accounts = new List<Account>() { new Account { Name = "Scrooge McDuck", Amount = 667377678765m }, new Account { Name = "Donald Duck", Amount = -200m } }; ImmutableList<Account> immutableAccounts = accounts.ToImmutableList();//列表创建一个不变集合 ImmutableList<Account>.Builder builder = immutableAccounts.ToBuilder();//创建构建器 for (int i = 0; i < builder.Count; i++) { Account a = builder[i]; if (a.Amount > 0) //集合中Amount的值大于0移除 { builder.Remove(a); } } ImmutableList<Account> overdrawnAccounts = builder.ToImmutable();//转换为不变集合 foreach (var item in overdrawnAccounts) { Console.WriteLine("{0} {1}", item.Name, item.Amount); } Console.ReadKey(); }
//示例2
ImmutableArray<string> a1 = ImmutableArray.Create<string>();
ImmutableArray<string> a2 = a1.Add("Williams");
ImmutableArray<string> a3 = a2.Add("Ferrari").Add("Mercedes").Add("Red Bull Racing");
10.13并发集合
1)ConcurrentQueue:线程安全的先进先出 (FIFO) 集合
主要方法:
Enqueue(T item);将对象添加到集合结尾。
TryDequeue(out T result); 尝试移除并返回位于集合开始处的对象,返回值表示操作是否成功。
TryPeek(out T result);尝试返回集合开始处的对象,但不将其移除,返回值表示操作是否成功。
说明:
ConcurrentQueue是完全无锁的,但当CAS操作失败且面临资源争用时,它可能会自旋并且重试操作。
ConcurrentQueue是FIFO集合,某些和出入顺序无关的场合,尽量不要用ConcurrentQueue。
2)ConcurrentStack:线程安全的后进先出 (LIFO) 集合
主要方法及属性:
Push(T item);将对象插入集合的顶部。
TryPop(out T result);尝试弹出并返回集合顶部的对象,返回值表示操作是否成功。
TryPeek(out T result);尝试返回集合开始处的对象,但不将其移除,返回值表示操作是否成功。
IsEmpty { get; }指示集合是否为空。
PushRange(T[] items);将多个对象插入集合的顶部。
TryPopRange(T[] items);弹出顶部多个元素,返回结果为弹出元素个数。
说明:
与ConcurrentQueue相似地,ConcurrentStack完全无锁的,但当CAS操作失败且面临资源争用时,它可能会自旋并且重试操作。
获取集合是否包含元素使用IsEmpty属性,而不是通过判断Count属性是否大于零。调用Count比调用IsEmpty开销大。
使用PushRange(T[] items)和TryPopRange(T[] items)时注意缓冲引起的额外开销和额外的内存消耗。
3) ConcurrentBag:元素可重复的无序集合
主要方法及属性:
TryPeek(out T result);尝试从集合返回一个对象,但不移除该对象,返回值表示是否成功获得该对象。
TryTake(out T result);尝试从集合返回一个对象并移除该对象,返回值表示是否成功获得该对象。
Add(T item);将对象添加到集合中。
IsEmpty { get; }解释同ConcurrentStack
说明:
ConcurrentBag为每一个访问集合的线程维护了一个本地队列,在可能的情况下,它会以无锁的方式访问本地队列。
ConcurrentBag在同一个线程添加和删除元素的场合下效率非常高。
因为ConcurrentBag有时会需要锁,在生产者线程和消费者线程完全分开的场景下效率非常低。
ConcurrentBag调用IsEmpty的开销非常大,因为这需要临时获得这个无序组的所有锁。
4)BlockingCollection:实现System.Collections.Concurrent.IProducerConsumerCollection<T> 的线程安全集合,提供阻塞和限制功能
主要方法及属性:
BlockingCollection(int boundedCapacity);boundedCapacity表示集合限制大小。
CompleteAdding();将BlockingCollection实例标记为不再接受任何添加。
IsCompleted { get; }此集合是否已标记为已完成添加并且为空。
GetConsumingEnumerable();从集合中移除并返回移除的元素
Add(T item);添加元素到集合。
TryTake(T item, int millisecondsTimeout, CancellationToken cancellationToken);
说明:
使用BlockingCollection()构造函数实例化BlockingCollection,意味着不设置boundedCapacity,那么boundedCapacity为默认值: int.MaxValue。
限界:使用BlockingCollection(int boundedCapacity),设置boundedCapacity的值,当集合容量达到这个值得时候,向BlockingCollection添加元素的线程将会被阻塞,直到有元素被删除。
限界功能可控制内存中集合最大大小,这对于需要处理大量元素的时候非常有用。
默认情况下,BlockingCollection封装了一个ConcurrentQueue。可以在构造函数中指定一个实现了IProducerConsumerCollection接口的并发集合,包括:ConcurrentStack、ConcurrentBag。
使用此集合包含易于无限制等待的风险,所以使用TryTake更加,因为TryTake提供了超时控制,指定的时间内可以从集合中移除某个项,则为 true;否则为 false。
5)ConcurrentDictionary:可由多个线程同时访问的键值对的线程安全集合。
主要方法
AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory);如果指定的键尚不存在,则将键/值对添加到 字典中;如果指定的键已存在,则更新字典中的键/值对。
GetOrAdd(TKey key, TValue value);如果指定的键尚不存在,则将键/值对添加到字典中。
TryRemove(TKey key, out TValue value);尝试从字典中移除并返回具有指定键的值。
TryUpdate(TKey key, TValue newValue, TValue comparisonValue);将指定键的现有值与指定值进行比较,如果相等,则用第三个值更新该键。
说明:
ConcurrentDictionary对于读操作是完全无锁的。当多个任务或线程向其中添加元素或修改数据的时候,ConcurrentDictionary使用细粒度的锁。使用细粒度的锁只会锁定真正需要锁定的部分,而不是整个字典。
6)IProducerConsumerCollection:定义供生产者/消费者用来操作线程安全集合的方法。 此接口提供一个统一的表示(为生产者/消费者集合),从而更高级别抽象如 System.Collections.Concurrent.BlockingCollection<T>可以使用集合作为基础的存储机制。
10.3.1管道
async 与 awair async主要修饰方法, awair表示等待 相关理解
(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。
(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。
(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。
(4)async要么是void,要么和Task关联
10.3.2使用BlockingCollection
1 在foreach循环中,用输入阻塞集合调用GetConsumingEnurnerable以迭化各项,好直接BlockingCollection变量,则只会迭代当前状态的集合,而不会迭代以后添加的项.
2 BlockingCollection.CompleteAdding方法是很重要的,否则读取器会在foreach循环中等待更多的项被添加