多读手册!
You have to pay for your decision. 你所做的一切决定,都是有代价的,或是性能,或是灵活性,没有最优解,你需要自己去权衡。
-暴王
这些一个个的零散的小点都是在日常工作中总结出来的,似乎没有哪一本编程书会讲这些,所以就总结出来放到这里
不要在循环里写循环体公用的初始化方法,应该在循环外只初始化一次
Wrong:
-
- foreach(string t in items)
- {
- List nameList = something.GetList();
- if(nameList.Contains(t))
- ...
- }
-
上面例子可以看到,每次循环都需要请求一次GetList(),并且获取到的List不会被更改,因此每次循环时nameList都是一样的,这时应该把GetList()方法的调用提出循环体
Correct:
-
- List nameList = something.GetList();
- foreach(string t in items)
- {
- if(nameList.Contains(t))
- ...
- }
-
优点: 简单
缺点: 任何更改对原值不起作用
-
- string a = "a";
- public void Dosomething(string a)
- {
- a = "b"
- }
- Console.WriteLine(a);
-
-
- string a = "aaaaa";
- string b = a.Replace('a', 'b');
- Console.WriteLine(a);
-
优点: 更改会对原值起作用,一次改动可以影响所有引用该对象的地方
缺点: 不注意会引起副作用
-
- object c = new object();
- object d = c;
- d = null;
- Console.WriteLine(c == null);
-
-
- public class Test
- {
- public string Field1 { get; set; }
- public string Field2 { get; set; }
-
- public Test(string field1, string field2)
- {
- Field1 = field1;
- Field2 = field2;
- }
-
- public void Dosomething(Test f)
- {
- f.Field1 = "3";
- f = null;
- f = new Test("5", "6");
- }
- }
-
- Test e = new Test("1", "2");
- e.Dosomething(e);
- Console.WriteLine(e == null);
- Console.WriteLine(e.Field1); ???????
-
优点: 保护了源数据不被更改
缺点: 序列化反序列化可能造成性能问题
-
- public class ProtectObject
- {
- private object valuableObject;
- public object ValuableObject
- {
- get
- {
- var objectCopy = JsonSerializer.Serialize(valuableObject);
- return JsonSerialize.Deserialize<object>(objectCopy);
- }
- }
- }
-
优点: 每次都能得到最新的值
缺点: 每次都重新计算值,可能造成性能问题
-
- class ContactInfo
- {
- public string Phonenumber;
- public string Name;
- public Address Address;
- public string AddressStr
- {
- get
- {
- return string.Format("{0} {1} {2} {4}", City, State, Address1, Address2);
- }
- }
- }
-
优点: 方便
缺点: 不好追踪值是在什么时候被改掉的
通过对缺失对象的封装,以提供默认无任何行为的对象替代品。
-
- namespace NullObjectPattern.Implementation1
- {
- public interface ILog
- {
- void Write(string message);
- }
-
-
- public class ConsoleLog : ILog
- {
- public void Write(string message)
- {
- Console.WriteLine(message);
- }
- }
-
-
- public class NullLog : ILog
- {
- public void Write(string message)
- {
- // do nothing
- }
- }
-
-
- public class Client
- {
- public void TestCase1()
- {
- ILog log1 = new ConsoleLog();
- ILog log2 = new NullLog();
- log1.Write("message to log");
- log2.Write("message to log");
- }
- }
- }
-
数据完整性
Wrong:
-
- class ContactInfo
- {
- public string Address 1;
- public string Address 2;
- public string State;
- public string City;
- public string Phonenumber;
- public string Name;
- public GetAddressString()
- {
- return string.Format("{0} {1} {2} {4}", City, State, Address1, Address2);
- }
- }
-
Right:
-
- class Address
- {
- public string Address 1;
- public string Address 2;
- public string State;
- public string City;
- }
- class ContactInfo
- {
- public string Phonenumber;
- public string Name;
- public Address Address;
- public string AddressStr
- {
- get
- {
- return string.Format("{0} {1} {2} {4}", City, State, Address1, Address2);
- }
- }
- }
-
-
- public class TestCollection
- {
- public List NumberList { get; private set; }
- public readonly List ReadOnlyNumberList;
- }
- public TestCollection()
- {
- NumberList = new List();
- ReadOnlyNumberList = new List();
- }
-
- TestCollection t1 = new TestCollection();
- t1.NumberList = new List();
- t1.ReadOnlyNumberList = new List&lint>();
- t1.NumberList.Add(1);
- t1.ReadOnlyNumberList.Add(2);
-
函数中的条件逻辑使人难以看清正常的执行途径。使用卫语句表现所有特殊情况。
动机:条件表达式通常有2种表现形式。第一:所有分支都属于正常行为。第二:条件表达式提供的答案中只有一种是正常行为,其他都是不常见的情况。
这2类条件表达式有不同的用途。如果2条分支都是正常行为,就应该使用形如if…..else…..的条件表达式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为“卫语句”。卫语句常用于数据校验,并且上面的逻辑“保卫”了下面的逻辑。
-
- void func(void)
- {
- if(IsWorkingDay())
- {
- printf("Error,is working day");
- }
- else
- {
- if(IsWorkingTime())
- {
- printf("Error ,is working time");
- }
- else
- {
- rest();
- }
- }
- }
-
使用卫语句
-
- void func()
- {
- if(IsWorkingDay())
- {
- printf("Error,is work day");
- return;
- }
-
- if(IsWorkingTime())
- {
- printf("Error,is work time");
- return ;
- }
-
- rest();
- }
-
-
- IContact
- {
- SaveContactInfo(int id, string name, string phoneNumber)
- GetContactInfoById(int id)
- }
-
增加了地址
Wrong:
-
- IContact
- {
- SaveContactInfo(int id, string name, string phoneNumber, string address1, string address2, string city, string state)
- GetContactInfoById(int id)
- }
-
Right:
-
- IContact
- {
- SaveContactInfo(ContactInfo contact)
- GetContactInfoById(int id)
- }
- class ContactInfo
- {
- public int Id;
- public string Name;
- public string PhoneNumber;
- public Address AddressInfo;
- }
- class Address
- {
- public string Address1;
- public string Address2;
- public string City;
- public string State;
- }
-
我们在日常编程中,如果想指定某个property或field不被外界修改,通常会使用readonly关键字,这样外界就不能对这个字段赋值了,但是对于对象或者集合来说,这个关键字虽然保证了property或者field本身不能被赋值,但是外部依旧可以修改对象中的property或者field或者修改集合,我们来看下面的代码
我们有个AClass类,它有一个readonly的对象AObject,和一个readonly的集合List,当我们在外部调用它时会发现,我们不能够直接对AObject或者List赋值,但是我们可以向List添加元素或者修改AObject的属性值。
Solution
对于集合,.net framework提供了readonly的封装,有很多以IReadOnlyXXX开头的集合接口,
- IReadOnlyList
- IReadOnlyCollection
- IReadOnlyDictionary
因此我们要做两件事来达到集合不被修改的目的
- IReadOnlyList ReadOnlyList
- { get; private set; }
对于对象,参考上面的传副本部分
在代码库里发现了类似这样的代码:
-
- class AObject
- {
- public AObject(string str)
- { this.AString = str; }
- public string AString;
- }
- List list = new List() { new AObject("a"), new AObject("b"), new AObject("c") };
- list.Where(o => o.AString == "a");
问题:
- list.Count?
答案是 3,因为 Where 操作不改变集合本身。如果想用 Where 的结果应该拿 Where 方法的返回值:
-
- list = list.Where(o => o.AString == "a");
在代码库里发现了这样的代码:
-
- class AObject
- {
- public AObject(string str)
- { this.AString = str; }
- public string AString;
- }
- List list = new List() { new AObject("a"), new AObject("b"), new AObject("c") };
- list.Select(o =>
- {
- o.AString = "d"
- return o;
- });
问题:
- list[0].AString?
答案是 “d”,因为 list 里面的元素是引用类型 AObject,Select 应该只做属性的选择或类型转换,不应该改变原集合里面元素的属性,如果要改变元素集合的属性,应该用 Each