数学的复习,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 = ;
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 = ;
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 = ; 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 * ;
} 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 = ; 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 * ;
} public static double Square(double value)
{
return value * value;
}
}
}
Func示例
8.2.5 BubbleSoter委托示例
class Program
{
static void Main()
{
Employee[] employees =
{
new Employee("Bugs Bunny", ),
new Employee("Elmer Fudd", ),
new Employee("Daffy Duck", ),
new Employee("Wile Coyote", 1000000.38m),
new Employee("Foghorn Leghorn", ),
new Employee("RoadRunner", )
}; 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 = ; i < sortArray.Count - ; i++)
{
if (comparison(sortArray[i + ], sortArray[i]))
{
T temp = sortArray[i];
sortArray[i] = sortArray[i + ];
sortArray[i + ] = 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;
}
}
BubbleSoter示例
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 * ;
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 = ;
Func<int, int> test = j => j * i;
Console.WriteLine(test());//输出12
i = ;
Console.WriteLine(test());//输出20
Console.ReadLine();
8.3.4 foreach闭包
var values = new list<int>() {,,};
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(); |
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=});
list.Add(new Stu(){Name="Li", Age=});
list.Add(new Stu(){Name="Wang", Age=});
list.Add(new Stu(){Name="Liu", Age=}); //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", ));
pdm.AddDocument(new Document("two", "Sample", ));
pdm.AddDocument(new Document("three", "Sample", ));
pdm.AddDocument(new Document("four", "Sample", ));
pdm.AddDocument(new Document("five", "Sample", ));
pdm.AddDocument(new Document("six", "Sample", ));
pdm.AddDocument(new Document("seven", "Sample", ));
pdm.AddDocument(new Document("eight", "Sample", )); 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>>();
for (int i = ; i < ; 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 < || priority >= ) throw new ArgumentException("优先级必须介于0到9之间。");
if (priorityNodes[priority].Value == null)
{
--priority;
if (priority >= )
{
//检查下一个较低优先级
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(, "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位的数组 00000000
bits1.SetAll(true); //全部位数为1 11111111
bits1.Set(, false); //设置位数1是0 11111101
bits1[] = 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 ? : );
}
}
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();
for (int i = ; i < ; i++)
{
if ((bits & 0x80000000) != ) //bits或运算最高位
{
sb.Append(""); //追加到结尾
}
else
{
sb.Append("");
}
bits = bits << ; //左移1位
}
string s = sb.ToString();
if (removeTrailingZero)
return s.TrimStart('');
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 = ; i < builder.Count; i++)
{
Account a = builder[i];
if (a.Amount > ) //集合中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循环中等待更多的项被添加