多读手册!
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