집 >백엔드 개발 >C#.Net 튜토리얼 >C# 성능 최적화 모범 사례
1. 메모리 누수를 방지하려면 명시적으로 등록된 EvenHandler를 명시적으로 등록 취소해야 합니다.
객체의 이벤트에 멤버 메서드를 등록하면 후자가 A 참조를 보유하게 됩니다. 전자에게. 전자는 이벤트가 로그아웃될 때까지 가비지 수집되지 않습니다.
private void Form1_Load() { …… //注册事件 CommandRemotingContext.CmdChanged += new ReciverCmdStateChangedEventHandler(this.CommandRemotingContext_CmdChanged); …… } private void Form1_FromClosed() { …… //关闭窗体时及时释放事件 CommandRemotingContext.CmdChanged -= new ReciverCmdStateChangedEventHandler(this.CommandRemotingContext_CmdChanged); …… }
이벤트로 인한 메모리 누수 문제:
객체 A가 객체 B의 이벤트를 구독합니다
객체 A의 수명 주기가 객체 B의 수명 주기보다 훨씬 깁니다
객체 A가 객체 B를 구독 취소하지 않는 시간
결국 객체 B를 구독 취소할 수 없게 됩니다. 출시 예정
2. 컨트롤에 바인딩된 데이터 소스의 일괄 작업은 자동 새로 고침을 피해야 합니다
클라이언트에서 데이터를 일괄적으로 운영할 경우 컨트롤의 새로 고침 작업으로 인해 불필요한 시간 소모가 발생하게 됩니다
데이터 소스(예: DataTable, Array, List, ObservableCollection 또는 기타 IListSource 등)가 컨트롤에 바인딩된 경우 데이터를 일괄적으로 작업할 때 바인딩 연결을 끊거나 컨트롤 새로 고침을 일시 중단해야 합니다.
rree3. 클라이언트와 서버 간의 통신 횟수를 줄입니다.
WebService 호출 수가 적을수록 항상 좋은 것은 아닙니다. 전송할 데이터의 양이 많은 경우 여러 호출로 분할하는 것이 좋습니다.
짧은 WebService 호출의 경우 상호 작용 수를 줄이기 위해 최대한 병합해야 합니다.
this.gcBillList.DataSource = null; DataRowCollection rows = this.ds.Tables[0].Rows; foreach (DataRow row in rows) { // DataRow数据操作 } this.gcBillList.DataSource = this.ds.Tables[0].DefaultView;
4. 통신 수를 줄입니다. 클라이언트와 서버 사이
필요하지 않은 경우 루프에서 WebService를 반복적으로 호출하지 마십시오
//多次调用了相同的WS txtCompanyName.Text=SCPubFunctionClient.PublicWSCal<string>(“ForTest”, “GetCompanyNameByID”,“0001”); txtCompanyInnerName.Text=SCPubFunctionClient.PublicWSCal<string>(“ForTest”, “GetCompanyInnerNameByID”,“0001”); //合并相邻的WS string[] result=SCPubFunctionClient.PublicWSCal<string>(“ForTest”, “GetCompanyNameAndInnerNameByID”,“0001”); txtCompanyName.Text=result[0]; txtCompanyInnerName.Text= result[1];
5. 박싱 및 언박싱 작업을 피하기 위해 제네릭을 사용합니다(가비지 수집 부담을 줄입니다)
박싱 작업으로 인해 GC 압력이 발생합니다. 컬렉션에서 발생하는 경우 일반 컬렉션을 사용하여 피해야 합니다.
값 유형 컬렉션의 경우 ArrayList 대신 List8742468051c85b06f0a0af9e3e506b5c를 사용하고 Hashtable 대신 Dictionaryb6842da76bed01162354d37c4f2d3464를 사용하세요.
//循环调用了相同的WS List<Person> persons; …… foreach(string personID in personIDs) { person=HRPubWsClient.getPerson(personID); persons.Add(person); } //合并WS List<Person> persons; …… persons =HRPubWsClient.getPersonList(personIDs);
6. 문자열 작업:
C# 문자열 작업 - 가비지 수집 부담 감소
7. 객체 생성을 피하기 위해 상수를 사용하세요
다음 예와 같이 프로그램에 새로운 십진수(0) 코드가 많이 있습니다. , 이로 인해 작은 객체가 자주 생성되고 재활용됩니다. 올바른 접근 방식은 Decimal.Zero 상수를 사용하는 것입니다.
ArrayList h=new ArrayList(); //不建议 h.Add(1); List<object> h = new List<object>(); //不建议 h.Add(1); List<int> h = new List<int>(); //建议 h.Add(1);
8. 불필요한 예외 방지
C# 예외 처리(Catch Throw) IL 분석
9. 여러 요소를 삭제하려면 RemoveAt 대신 RemoveAll을 사용하세요.
RemoveAll 메서드를 사용하는 경우 컬렉션(예: List)에서 여러 요소를 한 번에 삭제하려면 List의 내부 배열 크기만 한 번만 조정됩니다. 이는 루프에서 RemoveAt를 호출하는 것보다 훨씬 더 효율적입니다.
rree10. C# DataSet 성능 모범 사례
11. 반사 및 동적 바인딩
리플렉션 기술은 컴파일 시 정적 바인딩을 런타임으로 지연되는 동적 바인딩으로 변환하는 기술입니다.
C#은 주로 동적으로 객체를 생성하는 5가지 방법을 지원합니다. (시간 소모는 네트워크에서 발생하는데, 이는 제가 실제 측정한 것과는 상당히 다릅니다. 구체적인 테스트는 아래 참조) :
动态创建对象的方式 |
与Direct Create |
1.Type.InvokeMember |
慢40倍以上 |
2.ContructorInfo.Invoke |
慢40倍以上 |
3.Activator.CreateInstance(Type) |
慢7倍 |
4.Activator.CreateInstance(assemblyName, typeName) |
慢1000倍以上 |
5.Assembly.CreateInstance(typeName) |
慢40倍以上 |
应尽量避免使用反射和动态绑定;如必须使用,要遵循以下原则:
1. 使用接口调用方式将动态绑定改造为早期绑定(Direct Call)
2. 使用 Activator.CreateInstance(Type)方式动态创建对象
3. 使用typeof操作符代替GetType调用
小注:
通过循环创建实例记录时间如下:
加载程序集、获取类型在循环外部时间如下(这时不同创建方式消耗时间差距挺大):
代码如下:
public void TestCreateInstance() { Stopwatch watch1 = new Stopwatch(); var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); int num = 100000; watch1.Start(); for (int i = 0; i < num; i++) { //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); Activator.CreateInstance(type); } watch1.Stop(); label1.Text = "Activator.CreateInstance(Type type)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { Activator.CreateInstance("ReflectiveClassLibrary", "ReflectiveClassLibrary.TestClass"); } watch1.Stop(); label2.Text = "Activator.CreateInstance(string assemblyName,string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); //加载程序集 asmb.CreateInstance("TestClass"); } watch1.Stop(); label3.Text = "assembly.CreateInstance(string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); object obj = type.InvokeMember(null, BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null); } watch1.Stop(); label4.Text = "Type.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); ConstructorInfo constructorInfo = type.GetConstructors()[0]; constructorInfo.Invoke(null); } watch1.Stop(); label5.Text = "ContructorInfo.Invoke(object[] parameters)时间:" + watch1.ElapsedMilliseconds + "毫秒"; }
加载程序集、获取类型在循环内部时间如下(这时不同创建方式消耗时间差距比较小):
代码如下:
public void TestCreateInstance() { Stopwatch watch1 = new Stopwatch(); //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); int num = 100000; watch1.Start(); for (int i = 0; i < num; i++) { var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); Activator.CreateInstance(type); } watch1.Stop(); label1.Text = "Activator.CreateInstance(Type type)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { Activator.CreateInstance("ReflectiveClassLibrary", "ReflectiveClassLibrary.TestClass"); } watch1.Stop(); label2.Text = "Activator.CreateInstance(string assemblyName,string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); //加载程序集 asmb.CreateInstance("TestClass"); } watch1.Stop(); label3.Text = "assembly.CreateInstance(string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); object obj = type.InvokeMember(null, BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null); } watch1.Stop(); label4.Text = "Type.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args)时间:" + watch1.ElapsedMilliseconds + "毫秒"; watch1.Reset(); watch1.Start(); for (int i = 0; i < num; i++) { var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll"); Type type = asmb.GetType("ReflectiveClassLibrary.TestClass"); ConstructorInfo constructorInfo = type.GetConstructors()[0]; constructorInfo.Invoke(null); } watch1.Stop(); label5.Text = "ContructorInfo.Invoke(object[] parameters)时间:" + watch1.ElapsedMilliseconds + "毫秒"; }
测试代码如下:
c# 反射测试demo
12、序列化与反序列化
相对于XML、二进制序列化方式,Protobuf效率较高,支持数据量较大
protobuf序列化后的大小是json的1/10,xml格式的1/20,是二进制序列化的1/10
以上就是C# 性能优化最佳实践的内容,更多相关内容请关注PHP中文网(www.php.cn)!