一、概述:
C#基础我想每一个.NET程序猿都学习过,但是如何使用才是最优方法呢?往往这些基础知识被大家忽略。
怎样操作字符串?如何进行类型转换?什么是克隆?为什么需要HashCode?
今天我们就来系统的回顾一下基础知识。
二、实现:
1. 字符串拼接
下面我们看一下下面代码:
string a = "t"; string b = "e"; string c = "s"; //方法1 string result = a + b + c; //方法2 StringBuilder strBuilder = new StringBuilder(); strBuilder.Append(a); strBuilder.Append(b); strBuilder.Append(c); string resultBuilder = strBuilder.ToString(); //方法3, string.Format("{0}{1}{2}", a, b, c);
方法1,对字符串进行进行“=”或者“+”操作时,内存都会创建一个新的字符串对象并且分配新的空间。
方法2,StringBuilder不会重新创建一个string对象,而是以托管的方式分配内存
方法3,推选,Format内部使用StringBuilder实现的,但是操作更灵活清晰。
2. 使用默认类型转换
下面我们介绍一下Parse和TryParse哪个更实用
string str = string.Empty; string str1 = "123"; double d = 0D; long ticks; Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 1000; i++) { try { d = Double.Parse(str1); } catch { d = 0; } } sw.Stop(); ticks = sw.ElapsedTicks; Console.WriteLine("Parse()耗时:{0},执行结果:{1}", ticks, d); //这样做是安全的,但是结构太繁琐,让我们试试 sw = Stopwatch.StartNew(); for (int i = 0; i < 1000; i++) { if (!Double.TryParse(str1, out d)) { d = 0; } } sw.Stop(); ticks = sw.ElapsedTicks; Console.WriteLine("TryParse耗时:{0},执行结果:{1}", ticks, d);
输出结果:
Parse()耗时:32553653,执行结果:0
TryParse()耗时:638,执行结果:0
3. 区别对待强制转型与as和is
我们有两个类,
class FirstType { public string Name { get; set; } } class SecondType : FirstType { }
如何进行类型转换:
SecondType secondType = new SecondType() { Name = "Abel" }; FirstType firstType1 = (FirstType)secondType; FirstType firstType2 = secondType as FirstType;
很明显As更可读。
4.使用int?确保值类型也可以为null
数据存储中数据库所有变量的初始值都可以为null,包括基类型int,我们在程序中如何实现呢?
int? num = null; int cons = 0; num = cons; Console.WriteLine("int? num 被成功赋值:{0}", num); //反过来 int? consnum = 1234; int number; if (consnum.HasValue) { number = consnum.Value; } else { number = 0; } //number = consnum ?? 0; Console.WriteLine("number被成功赋值:{0}", number);
代码中备注掉的部分(number = consnum ?? 0)与上面的if实现功能一样,但更简洁。
与双目运算符(?:)差不多,consnum.HasValue为true则将consnum.Value值赋给number,否则number的值为0
5.区分readonly与const的使用方法
const是一个变异期的常量,readonly是运行期的常量
const至修饰基元类型,枚举,字符串类型,readonly没有限制。
const是编译型常量所以它天然就是static的,不能手动再为const添加static修饰。
readonly的全部意义在于运行第一次赋值后将不会改变。当然不可改变分两层意思:对于基类型变量本身不能改变,对于引用类型本身指针不可改变。
6.运算符重载:
我们实现对象相加重载
class OperatorSalary { public float RMB { get; set; } public static OperatorSalary operator +(OperatorSalary s1, OperatorSalary s2) { s1.RMB += s2.RMB; return s1; } }
调用代码:
OperatorSalary mikeincom = new OperatorSalary() { RMB = 22 }; OperatorSalary roseincom = new OperatorSalary() { RMB = 33 }; OperatorSalary allincom = mikeincom + roseincom; Console.WriteLine("工资和为: {0}", allincom.RMB);
这样我们实现对象相加重载,基本运算重载都是类似的。
7.对象比较器:
对象比较器重写需要继承接口IComparable<T>和IComparer<T>
class Salary : IComparable<Salary> { public string Name { get; set; } public float BaseSalary { get; set; } public float Bonus { get; set; } public int CompareTo(Salary other) { return this.BaseSalary.CompareTo(other.BaseSalary); } } class BonusComparer : IComparer<Salary> { public int Compare(Salary x, Salary y) { return x.Bonus.CompareTo(y.Bonus); } }
实现代码如下:
List<Salary> CompanySalary = new List<Salary>(){ new Salary{Name="Abel",BaseSalary=20000.10f,Bonus=1000f}, new Salary{Name="Tomson",BaseSalary=40000.10f,Bonus=2000f}, new Salary{Name="Lucy",BaseSalary=30000.10f,Bonus=4000f}, new Salary{Name="Erwin",BaseSalary=60000.10f,Bonus=5000f}, new Salary{Name="Steven",BaseSalary=80000.10f,Bonus=3000f} }; //首先我们以基本工资排序 CompanySalary.Sort(); foreach (Salary item in CompanySalary) { Console.WriteLine(string.Format("Name:{0} Basesalary:{1} Bonus:{2}",item.Name,item.BaseSalary,item.Bonus)); } //然后我们以奖金排序 CompanySalary.Sort(new BonusComparer()); foreach (Salary item in CompanySalary) { Console.WriteLine(string.Format("Name:{0} Basesalary:{1} Bonus:{2}", item.Name, item.BaseSalary, item.Bonus)); }
8.区别对待==和Equals
重写Equals时也需要重写GetHashCode这需要继承IEquatable<T>泛型接口
class PersonMoreInfo { public string SomeInfo { get; set; } } class Person:IEquatable<Person> { public string IDcode { get; private set; } public string Name { get; set; } public Person(string idcode) { this.IDcode = idcode; } public override bool Equals(object obj) { return this.IDcode==(obj as Person).IDcode; } public bool Equals(Person otherPerson) { return this.IDcode == otherPerson.IDcode; } public override int GetHashCode() { //return this.IDcode.GetHashCode();// 它永远返回一个整型类型,而整型显然无法满足字符串的容量,这是可能返回相同的Hashcode。 //下面写法避免HashCode重复: return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDcode).GetHashCode(); } }
实现代码:
Dictionary<Person, PersonMoreInfo> PersonValue = new Dictionary<Person, PersonMoreInfo>(); PersonMoreInfo abelValue = new PersonMoreInfo() { SomeInfo = "Mike's info" }; Person Abel = new Person("NB0903100006"); Person Erwin = new Person("NB0903100006"); PersonValue.Add(Abel, abelValue); Console.WriteLine("Abel的HashCode:",Abel.GetHashCode()); Console.WriteLine(string.Format("Abel和Erwin{0}", Abel.Equals(Erwin) ? "相等" : "不相等")); Console.WriteLine(string.Format("Abel和Erwin{0}", PersonValue.ContainsKey(Erwin) ? "相等" : "不相等"));
9.格式化字符串
重写ToString()方法需要继承IFormatttable接口
class People : IFormattable { public string IDCode { get; set; } public Name Name { get; set; } //实现接口IFormattable的ToString public string ToString(string nameType, IFormatProvider formatProvider) { NameWord peopleNameToString = new NameWord(); var method=typeof(NameWord).GetMethod(nameType); object obj=new object(); obj = this; return method.Invoke(peopleNameToString, new object[] { obj }).ToString(); } } class Name { public string FirstName { get; set; } public string LastName { get; set; } } class NameWord { public string chineseName(People people) { return string.Format("中文名字:{0}", people.Name.FirstName); } public string englishName(People people) { return string.Format("英文名字:{0}.{1}", people.Name.FirstName,people.Name.LastName); } public string japaneseName(People people) { return string.Format("日语名字:{0}.{1}", people.Name.FirstName, people.Name.LastName); } public string franceName(People people) { return string.Format("法语名字:{0}.{1}", people.Name.FirstName, people.Name.LastName); } public string elseName(People people) { return this.ToString(); } }
代码实现:
People abelzhang = new People() { IDCode = "123", Name = new Name() { FirstName="zhang",LastName="Abel"} }; Console.WriteLine(abelzhang.ToString("chineseName", null));
这里本想用反射和表驱动法代替Switch,但是重写ToString方法发参数必须是string类型的,无法使用enum类型。
希望匠友们给出宝贵意见。
10.克隆:正确实现前拷贝和深拷贝
[Serializable] class Employee : ICloneable { public string IDCode { get; set; } public int Age { get; set; } public Department department { get; set; } public object Clone() { return this.MemberwiseClone(); }
//用序列化实现深拷贝 public Employee DeepClone() { using(Stream objectStream=new MemoryStream() ) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(objectStream, this); objectStream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(objectStream) as Employee; } }
//实现浅拷贝 public Employee ShallowClone() { return Clone() as Employee; } } [Serializable] class Department { public string Name { get; set; }
public override string ToString() { return this.Name.ToString(); } }
这里深拷贝用序列化实现的。
Employee AbelCopy = new Employee() { IDCode = "123", Age = 26, department = new Department() { Name="Abel"} }; Employee LucyCopy = AbelCopy.ShallowClone(); Employee ErwinCopy = AbelCopy.DeepClone(); Console.WriteLine("LucyCopy---ID:{0},Age:{1},Department:{2}", LucyCopy.IDCode, LucyCopy.Age, LucyCopy.department.Name); Console.WriteLine("ErwinCopy---ID:{0},Age:{1},Department:{2}", ErwinCopy.IDCode, ErwinCopy.Age, ErwinCopy.department.Name); AbelCopy.IDCode = "321"; AbelCopy.Age = 27; AbelCopy.department.Name = "Lucy"; Console.WriteLine("LucyCopy---ID:{0},Age:{1},Department:{2}", LucyCopy.IDCode, LucyCopy.Age, LucyCopy.department.Name); C
11.简单的反射实现
我们先有一个普通的类。
public class DynamicSample { public string Name { get; set; } public int Add(int a, int b) { return a + b; } }
实现Add方法的反射
int times = 1000000; DynamicSample reSample = new DynamicSample(); var addMethod= typeof(DynamicSample).GetMethod("Add"); Stopwatch WatchDynamic1=Stopwatch.StartNew(); for (int i = 0; i < times; i++) { addMethod.Invoke(reSample,new object[]{1,2}); } WatchDynamic1.Stop(); Console.WriteLine(string.Format("反射耗时:{0}毫秒", WatchDynamic1.ElapsedMilliseconds)); dynamic dySample = new DynamicSample(); Stopwatch WatchDynamic2 = Stopwatch.StartNew(); for (int j = 0; j < times; j++) { dySample.Add(1,2); } WatchDynamic2.Stop(); Console.WriteLine(string.Format("dynamic耗时:{0}毫秒", WatchDynamic2.ElapsedMilliseconds));
输出结果:
反射耗时:3490毫秒
dynamic耗时:330毫秒
从上面的例子分析dynamic实现反射的效率更高,但是这只适用于简单的反射,更复杂的反射还需要上面的方法处理。