**重写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 PersonValues = new Dictionary();
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
{
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 lists = new List();
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 lists = new List();
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 lists = new List();
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 lists = new List();
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;
///
/// 实现IDisposable中的Dispose方法
///
public void Dispose()
{
//必须为true
Dispose(true);
//通知垃圾回收机制不再调用终结器(析构器)
GC.SuppressFinalize(this);
}
///
/// 不是必要的,提供一个Close方法仅仅是为了更符合其他语言(如
/// C++)的规范
///
public void Close()
{
Dispose();
}
///
/// 必须,防止程序员忘记了显式调用Dispose方法
///
~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 fooLazy;
private A() { }
static A() { fooLazy = new Lazy(() => 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();
}
}
}
}