**重寫Equals時也應重寫GetHasgCode**
如果對象要作爲Dictionary的Key值,那麼重寫Equals時也應重寫GetHashCode。比如下列代碼,人的身份ID一樣應該就是同一個人,那麼我們期望得到的輸出是true,true。但是不重寫GetHasgCode,得到的輸出是true,false。因爲Dictionary是根據先Key值的HashCode再根據Equals來查找value。找不到對應的HashCode當然找不出Value。
namespace Tip12
{
class Program
{
static Dictionary<Person, PersonMoreInfo> PersonValues = new Dictionary<Person, PersonMoreInfo>();
static void Main(string[] args)
{
Person mike = new Person("500221");
PersonMoreInfo mikeValue = new PersonMoreInfo() { SomeInfo = "Mike's info" };
PersonValues.Add(mike, mikeValue);
//Console.WriteLine(mike.GetHashCode());
Console.WriteLine(PersonValues.ContainsKey(mike));
Person mike1 = new Person("500221");
//Console.WriteLine(mike.GetHashCode());
Console.WriteLine(PersonValues.ContainsKey(mike1));
Console.Read();
}
}
class Person : IEquatable<Person>
{
public string IDCode { get; private set; }
public Person(string idCode)
{
this.IDCode = idCode;
}
public override bool Equals(object obj)
{
return IDCode == (obj as Person).IDCode;
}
//public override int GetHashCode()
//{
// return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDCode).GetHashCode();
//}
public bool Equals(Person other)
{
return IDCode == other.IDCode;
}
}
class PersonMoreInfo
{
public string SomeInfo { get; set; }
}
}
分情況(比如只是用於輸出)可多用linq語句來代替比較器,簡化代碼
Linq語句看情況用first(),take()來避免不必要的迭代
first()找到第一個,take(n)找到n個就完成。不需要遍歷所有
小心閉包陷阱
static void Main(string[] args)
{
List<Action> lists = new List<Action>();
for (int i = 0; i < 5; i++)
{
Action t = () =>
{
Console.WriteLine(i.ToString());
};
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
Console.Read();
}
//輸出爲55555
因爲如果匿名方法引用了某個局部變量,編譯器會自動將改引用提升到改閉包對象中,即將for循環中變量i修改爲引用閉包對象的公共變量i。等同於
static void Main(string[] args)
{
List<Action> lists = new List<Action>();
TempClass tempClass = new TempClass();
for (tempClass.i = 0; tempClass.i < 5; tempClass.i++)
{
Action t = tempClass.TempFuc;
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
Console.Read();
}
class TempClass
{
public int i;
public void TempFuc()
{
Console.WriteLine(i.ToString());
}
}
如果想輸出01234,可以將閉包對象的產生放在for循環內部。代碼的如下
static void Main(string[] args)
{
List<Action> lists = new List<Action>();
for (int i = 0; i < 5; i++)
{
int temp = i;
Action t = () =>
{
Console.WriteLine(temp.ToString());
};
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
Console.Read();
}
//輸出爲01234
等同於
static void Main(string[] args)
{
List<Action> lists = new List<Action>();
for (int i = 0; i < 5; i++)
{
TempClass tempClass = new TempClass();
tempClass.i = i;
Action t = tempClass.TempFuc;
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
Console.Read();
}
class TempClass
{
public int i;
public void TempFuc()
{
Console.WriteLine(i.ToString());
}
}
泛型協變與逆變
除非考慮到不會用於可變性,否則爲泛型參數指定out關鍵字會拓展其應用,建議在實際編碼中永遠這樣使用。
支持協變(out)的類型參數只能用在輸出位置:函數返回值、屬性的get訪問器以及委託參數的某些位置
支持逆變(in)的類型參數只能用在輸入位置:方法參數或委託參數的某些位置中出現。
釋放資源,實現IDisposable接口
public class SampleClass : IDisposable
{
//演示創建一個非託管資源
private IntPtr nativeResource = Marshal.AllocHGlobal(100);
//演示創建一個託管資源
private AnotherResource managedResource = new AnotherResource();
private bool disposed = false;
/// <summary>
/// 實現IDisposable中的Dispose方法
/// </summary>
public void Dispose()
{
//必須爲true
Dispose(true);
//通知垃圾回收機制不再調用終結器(析構器)
GC.SuppressFinalize(this);
}
/// <summary>
/// 不是必要的,提供一個Close方法僅僅是爲了更符合其他語言(如
/// C++)的規範
/// </summary>
public void Close()
{
Dispose();
}
/// <summary>
/// 必須,防止程序員忘記了顯式調用Dispose方法
/// </summary>
~SampleClass()
{
//必須爲false
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
// 清理託管資源
if (managedResource != null)
{
managedResource.Dispose();
managedResource = null;
}
}
// 清理非託管資源
if (nativeResource != IntPtr.Zero)
{
Marshal.FreeHGlobal(nativeResource);
nativeResource = IntPtr.Zero;
}
//讓類型知道自己已經被釋放
disposed = true;
}
}
class AnotherResource : IDisposable
{
public void Dispose()
{
}
}
標準的協作式取消,CancellationTokenSource
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
Thread t = new Thread(() =>
{
while (true)
{
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine("線程被終止!");
break;
}
Console.WriteLine(DateTime.Now.ToString());
Thread.Sleep(1000);
}
});
t.Start();
Console.ReadLine();
cts.Cancel();
}
Lazy模式的單例
class A
{
static readonly Lazy<A> fooLazy;
private A() { }
static A() { fooLazy = new Lazy<A>(() => new A()); }
public static A instance { get { return fooLazy.Value; } }
}
跨線程訪問控件
使用控件的Invoke方法或BeginInvoke方法,BeginInvoke是異步
if (this.InvokeRequired)
{
this.Invoke(new Action(() => button1.Enabled = false));//在擁有此控件的基礎窗口句柄的線程上執行指定的委託
button1.Invoke(new MethodInvoker(() => button1.Enabled = false ));
button1.Invoke(new Action(() => button1.Enabled = false)); // 跨線程訪問UI控件
}
else
{
button1.Enabled = false
}
button1.BeginInvoke(new Action(() => button1.Enabled = false));
//在創建控件的基礎句柄所在線程上**異步**執行指定委託
避免在構造方法中調用虛成員
下面的代碼會拋異常。因爲創建子類對象時,先執行父類構造方法,父類構造方法裏調用了虛方法,此時會執行子類override的方法。子類override的方法裏又調用了子類的字段,而此時子類的字段還爲null
class Program
{
static void Main()
{
American american = new American();
Console.ReadKey();
}
class Person
{
public Person()
{
InitSkin();
}
protected virtual void InitSkin()
{
//省略
}
}
class American : Person
{
Race Race;
public American()
: base()
{
Race = new Race() { Name = "White" };
}
protected override void InitSkin()
{
Console.WriteLine(Race.Name);
}
}
class Race
{
public string Name { get; set; }
}
}
可以加checked在運算溢出時拋出異常System.OverflowException
static void Main(string[] args)
{
ushort salary = 65535;
try
{
checked
{
salary = (ushort)(salary + 1);
}
}
catch(Exception ex)
{
}
}
Md5加密
是不可逆的,多對一(小概率,忽略不計)的加密。
比如存儲密碼時Md5加密後再存儲,這樣後臺也看不到密碼。但是可用窮舉法破解
static string GetMd5Hash(string input)
{
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
return BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(input))).Replace("-", "");
}
}
多次使用Md5,增加窮舉法破密成本
static string GetMd5Hash(string input)
{
string hashKey = "Aa1@#$,.Klj+{>.45oP";
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
string hashCode = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(input))).Replace("-", "") + BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(hashKey))).Replace("-", "");
return BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(hashCode))).Replace("-", "");
}
}
還可以驗證文件是否被篡改,用文件內容算出對應的Md5,文件哪怕被改動一個字符,再算出的Md5也不一樣
public static string GetFileHash(string filePath)
{
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", "");
}
}
對稱加密
class Program
{
static void Main()
{
EncryptFile(@"c:\temp.txt", @"c:\tempcm.txt", "123");
Console.WriteLine("加密成功!");
DecryptFile(@"c:\tempcm.txt", @"c:\tempm.txt", "123");
Console.WriteLine("解密成功!");
}
//緩衝區大小
static int bufferSize = 128 * 1024;
//密鑰salt
static byte[] salt = { 134, 216, 7, 36, 88, 164, 91, 227, 174, 76, 191, 197, 192, 154, 200, 248 };
//初始化向量
static byte[] iv = { 134, 216, 7, 36, 88, 164, 91, 227, 174, 76, 191, 197, 192, 154, 200, 248 };
//初始化並返回對稱加密算法
static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt, "SHA256", 1000);
SymmetricAlgorithm sma = Rijndael.Create();
sma.KeySize = 256;
sma.Key = pdb.GetBytes(32);
sma.Padding = PaddingMode.PKCS7;
return sma;
}
static void EncryptFile(string inFile, string outFile, string password)
{
using (FileStream inFileStream = File.OpenRead(inFile), outFileStream = File.Open(outFile, FileMode.OpenOrCreate))
using (SymmetricAlgorithm algorithm = CreateRijndael(password, salt))
{
algorithm.IV = iv;
using (CryptoStream cryptoStream = new CryptoStream(outFileStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] bytes = new byte[bufferSize];
int readSize = -1;
while ((readSize = inFileStream.Read(bytes, 0, bytes.Length)) != 0)
{
cryptoStream.Write(bytes, 0, readSize);
}
cryptoStream.Flush();
}
}
}
static void DecryptFile(string inFile, string outFile, string password)
{
using (FileStream inFileStream = File.OpenRead(inFile), outFileStream = File.OpenWrite(outFile))
using (SymmetricAlgorithm algorithm = CreateRijndael(password, salt))
{
algorithm.IV = iv;
using (CryptoStream cryptoStream = new CryptoStream(inFileStream, algorithm.CreateDecryptor(), CryptoStreamMode.Read))
{
byte[] bytes = new byte[bufferSize];
int readSize = -1;
int numReads = (int)(inFileStream.Length / bufferSize);
int slack = (int)(inFileStream.Length % bufferSize);
for (int i = 0; i < numReads; ++i)
{
readSize = cryptoStream.Read(bytes, 0, bytes.Length);
outFileStream.Write(bytes, 0, readSize);
}
if (slack > 0)
{
readSize = cryptoStream.Read(bytes, 0, (int)slack);
outFileStream.Write(bytes, 0, readSize);
}
outFileStream.Flush();
}
}
}
}