ホームページ >バックエンド開発 >C#.Net チュートリアル >C# パフォーマンス最適化のベスト プラクティス

C# パフォーマンス最適化のベスト プラクティス

黄舟
黄舟オリジナル
2017-02-25 11:04:451918ブラウズ

1. メモリリークを避けるために、明示的に登録された EvenHandler は明示的に登録解除される必要があります

オブジェクトのイベントにメンバーメソッドを登録すると、後者は前者への参照を保持します。前者は、イベントがログアウトされるまでガベージ コレクションされません。

rreeeememoryイベントによって引き起こされる漏れの問題:


  • オブジェクトbのイベントに登録するaubjectオブジェクトAのライフサイクルはオブジェクトBよりもはるかに長い

  • オブジェクト A はオブジェクト B からサブスクライブを解除しません

  • 最終的に、オブジェクト B を解放できなくなります

  • 2. コントロールにバインドされたデータ ソースのバッチ操作は自動更新を回避する必要があります


クライアントがデータをバッチで操作する場合、コントロールの更新操作により不必要な時間が消費されます

  • データソース(DataTable、Array、List、ObservableCollectionまたは他のIListSourceなど)が、など) ) がコントロールにバインドされている場合、データをバッチで操作する場合は、バインドを切断するか、コントロールの更新を一時停止する必要があります。

    private void Form1_Load()
    {
      ……
      //注册事件
      CommandRemotingContext.CmdChanged += new ReciverCmdStateChangedEventHandler(this.CommandRemotingContext_CmdChanged);
      ……
    }
    private void Form1_FromClosed()
    {
      ……
      //关闭窗体时及时释放事件
      CommandRemotingContext.CmdChanged -= new ReciverCmdStateChangedEventHandler(this.CommandRemotingContext_CmdChanged);
      ……
    }

  • 3. クライアントとサーバー間の通信の数を減らす

送信されるデータの量が多い場合は、分割することを検討してください。複数の呼び出しに分割します


  • 短い 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. オブジェクトの作成を避けるために定数を使用する

以下の例のように、プログラム内の多くの部分で新しい decimal(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 の内部配列に対してサイズ変更操作が 1 回だけ実行されます。これは、ループ内で RemoveAt を呼び出すよりも大幅に効率的です。

private string CurrencyCalc()
{
    if (firstValue == new decimal(0))  ……
    if (secondValue == new decimal(0)) ……
    if (thirdValue == new decimal(0)) ……
    if (fourthValue == new decimal(0)) ……
    ……
}

    10. C# DataSet のパフォーマンスのベスト プラクティス
  • 11. リフレクションと動的バインディング -- CPU 使用率を削減

リフレクション テクノロジは、コンパイル時の静的バインディングを実行時の動的バインディングの遅延に変換します。

  • 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)!


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。