目录
5.用FormattableString取代专门为特定区域而写的字符串
10.只有在应对新版基类与现有子类之间的冲突时才应该使用new修饰符
1.优先使用隐式类型的局部变量
用var来声明变量而不指明其类型,可以令开发者把注意力更多的集中在名称上。
var不是动态变量,而是编译器来根据右侧的类型推断。
2.考虑用readonly代替const
运行时的常量用readonly来声明,更为灵活。因为readonly是在执行构造函数后才不能被修改。类型不受限制,可以是随意的class
编译期的常量用const来声明(const可以在方法体内声明,readonly不行),常量是编译器把变量给替换成常量值,如果不同程序集都使用某个其他程序集的常量。而修改了常量值不编译其他程序集就会导致不一致。
3.优先考虑is或as运算符,尽量少用强制类型转换
强制类型转换有异常的情况,需要try/catch.而且要判断是不是等于Null
foreach内部使用的是强制类型转换,可能有历史包袱的原因。
比如
Int arr = {1,2,3,4};
foreacr (object I in arr)
是成立的。
as用于类型转换
is用于判断某个运行时类型是不是能转成目标类型,遵循多态规则。
4.用内插字符串取代string.Format()
String.Format()的不足
int a=0, b = 0;
string.Format("{1}{2}", a);
这种写法成立,由于有多个string.Format重载,编译器只会确定重载方法是否有合适的,不会检查参数个数与序号数量是否匹配。不匹配,就会抛出异常。
还有那种SQL语句的字符串拼接,可能有很多序号,需要一个一个的去核对序号是不是正确。容易出错。
内插字符串非常方便
String str =$"{var1}{var2}";
简短,不会出错
会把其他类型都转成string类型。
5.用FormattableString取代专门为特定区域而写的字符串
内插字符串默认的类型是FormattableString,为了支持多区域和语言。
6.不要用表示符号名称的硬字符串来调用API
也就是不要直接硬编码某些参数。多使用nameof()可以返回字符串。这样后期修改不会因为大量硬编码导致漏改,增大重复工作量。
还有MVC中的Route
7.用委托表示回调
Action可以接收任意数量的参数,返回值为void
Func<T,T> 接收参数然后计算出返回值的委托,
predicate判断某条件是否成立的bool返回值委托,LINQ中很多
所有的委托都是继承自Multicastdelegate,都是多播委托。
有两个坑:
1.程序调用委托的过程中可能发生异常。
2.多播委托,如果有返回值,前面的返回值都被丢弃,最后一个执行委托的返回值被返回。
8.用null条件运算符调用事件处理程序
老版写法 if(variable !=null){}
如果变量是事件处理器,多线程的环境下,有可能其他线程正好取消事件订阅,而执行判断的线程被打断了。 非常坑
新版写法 variable?.Invoke(this,num);
可惜不能variable?();
9.尽量避免装箱与拆箱
尽量不要再使用object的地方使用值类型
2.
public struct Person
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
public class Test
{
public static void Main()
{
List<Person> attendees = new();
Person p = new Person { Name = "Old Name" };
attendees.Add(p);
Person p2 = attendees[0];
p2.Name = "New Name";
Console.WriteLine(attendees[0].ToString());
}
}
这段打印出Old Name
泛型未装箱,而取值的时候又是一份拷贝对象。
10.只有在应对新版基类与现有子类之间的冲突时才应该使用new修饰符
使用new关键字去覆盖基类的行为是不符合里氏替换原则的。但还是有特殊情况
比如说 继承的基类新加了方法,属性,而这新加的在原来的子类中有了,那么折中的做法就是new覆盖了