web-dev-qa-db-ja.com

私の考えをC ++からC#に移行する方法

私は経験豊富なC++開発者であり、言語を非常に詳しく知っており、その特定の機能のいくつかを集中的に使用しています。また、私はOODの原則とデザインパターンを知っています。今はC#を学習していますが、C++の考え方を取り除けないという気持ちを止めることはできません。私はC++の強みに固執しすぎて、一部の機能なしでは生きられないほどでした。また、C#で適切な回避策や代替策を見つけることができません。

C++の観点からC#で異なるグッドプラクティスデザインパターンイディオムは何を示唆できますか? C#でばかげていない完璧なC++デザインを取得する方法?

具体的には、(最近の例)対応するC#風の良い方法が見つかりません。

  • 確定的なクリーンアップを必要とするリソース(ファイルなど)の寿命を制御する。これはusingを手に入れるのは簡単ですが、リソースの所有権が転送されているときにそれを適切に使用する方法[...スレッド間]? C++では、共有ポインターを使用するだけで、適切なタイミングでガベージコレクションを処理します。
  • 特定のジェネリックの関数をオーバーライドすることに常に苦労しています(C++での部分的なテンプレートの特殊化などが大好きです) C#で一般的なプログラミングを行う試みを放棄する必要がありますか?多分ジェネリックは意図的に制限されており、問題の特定のドメインを除いてジェネリックを使用するのはC#風ではありませんか?
  • マクロのような機能。一般に悪い考えですが、問題の一部のドメインでは、他の回避策はありません(たとえば、デバッグリリースにのみ行くべきログの場合のような、ステートメントの条件付き評価)。それらがないことは、私がより多くのif (condition) {...}ボイラープレートを配置する必要があることを意味しますが、それでも副作用のトリガーに関しては等しくありません。
12
Andrew

私の経験では、これを行うための本はありません。フォーラムはイディオムやベストプラクティスに役立ちますが、結局は時間を費やす必要があります。 C#でさまざまな問題を解決して練習します。何が難しい/ぎこちないものであるかを見て、次回はそれらをより難しくなく、よりぎこちなくするようにしてください。私にとって、このプロセスは私が「理解する」までに約1か月かかりました。

最も注目すべきことは、メモリ管理の欠如です。 C++では、これは設計上のすべての決定を支配しますが、C#では逆効果です。 C#では、オブジェクトの死または他の状態遷移を無視することがよくあります。この種の関連付けは、不必要な結合を追加します。

ほとんどの場合、古いツールへのアタッチメントに打ち勝つことなく、新しいツールを最も効果的に使用することに焦点を当てます。

C#でばかげていない完璧なC++デザインを取得する方法?

異なるイディオムを実現することにより、異なる設計アプローチに向けて推進します。 (ほとんどの)言語間で1対1に移植して、もう一方の端で美しく慣用的なものを期待することはできません。

しかし、特定のシナリオに応じて:

共有されたアンマネージリソース-これはC++では悪い考えであり、C#でも同じくらい悪い考えです。ファイルがある場合は、それを開いて使用し、閉じます。スレッド間でそれを共有することは問題を引き起こすだけであり、1つのスレッドだけが実際にそれを使用している場合は、そのスレッドを開いたり、所有したり、閉じたりします。何らかの理由でreallyの寿命の長いファイルがある場合は、それを表すクラスを作成し、アプリケーションに必要に応じてそれを管理させます(終了する前に閉じ、Application Closing sortに近づけます)イベントなど)

部分的なテンプレートの特殊化-ここでは運が悪いですが、C#を使用するほど、C++で行ったテンプレートの使用法を見つけることができますYAGNIに陥る。 Tがalwaysintであるときに、なぜ一般的なものにするのですか?他のシナリオは、インターフェースの自由なアプリケーションによってC#で最もよく解決されます。インターフェイスまたはデリゲート型が変化させたいものを強く型付けできる場合、ジェネリックは必要ありません。

プリプロセッサ条件-C#には#if、 怒る。さらに良いのは、コンパイラシンボルが定義されていない場合にコードからその関数を単に削除する 条件付き属性 があることです。

16
Telastyn

私の知る限り、確定的なライフタイム管理を可能にするのはIDisposableだけです。これは、気づいたように、そのようなリソースの所有権を譲渡する必要があるときに機能しなくなります(言語や型システムのサポートがないなど)。

あなたと同じボートにいる-C++開発者がC#atmを実行する。 -C#のタイプ/シグネチャで所有権移転(ファイル、db-接続など)をモデル化するためのサポートの欠如は、私にとって非常に不愉快ですが、「ただ」信頼するだけで十分機能することを認めざるを得ません。これを正しくするための規約とAPIドキュメント.

  • IDisposableを使用して確定的なクリーンアップを行いますiffが必要です。
  • IDisposableをいわば「キャンセル」できないため、usingの所有権を譲渡する必要がある場合は、関係するAPIが明確であることを確認し、usingを使用しないでください。

はい、それはあなたが現代のC++で型システムで表現できるもの(セマティックスの移動など)ほどエレガントではありませんが、それは仕事です、そして(私には驚くほど)C#コミュニティ全体はそうではないようですAFAICT。

4
Martin Ba

2つの特定のC++機能について言及しました。 RAIIについて説明します。

C#はガベージコレクションされるため、通常は適用されません。最も近いC#の構成は、おそらくIDisposableインターフェイスであり、次のように使用されます。

using (FileStream fs = File.OpenRead(@"c:\path\filename.txt"))
{
   //Do stuff with the file.
} //File will be closed here.

マネージドリソースには必要ないため、IDisposableを頻繁に実装することは期待しないでください(実装は少し進んでいます)。ただし、usingを実装する場合は、Dispose構文を使用するか、usingを実行できない場合はIDisposableを自分で呼び出す必要があります。これを台無しにしたとしても、適切に設計されたC#クラスは(ファイナライザを使用して)これを処理しようとします。ただし、ガベージコレクションされた言語では、RAIIパターンは完全に機能しないため(コレクションは非決定的であるため)、リソースのクリーンアップが遅延します(場合によっては致命的)。

2
Brian

多くのC++開発者がひどいC#コードを書くのを見たので、これは非常に良い質問です。

C#を「より優れた構文を備えたC++」と考えないことが最善です。彼らはあなたの思考に異なるアプローチを必要とする異なる言語です。 C++は、CPUとメモリが何をするかについて常に考えていることを強制します。 C#はそうではありません。特にC#は、CPUとメモリを考慮せず、代わりに作成しているビジネスドメインを考慮して設計されています。

私が見たこの一例は、多くのC++開発者がforeachよりも高速であるため、forループを使用することを好みます。これは通常、C#では悪い考えです。反復されるコレクションの可能なタイプが制限されるため(そしてコードの再利用性と柔軟性)。

C++からC#に再調整する最良の方法は、異なる視点からコーディングを試すことです。最初はこれは難しいでしょう。何年にもわたって、作成したコードをフィルター処理するために「CPUとメモリの機能」スレッドを頭の中で使用することに完全に慣れているからです。しかし、C#では、ビジネスドメイン内のオブジェクト間の関係について考える必要があります。 「コンピュータは何をしているのか」ではなく「何をしたいのか」。

オブジェクトのリストのすべてに対して何かをしたい場合は、リストにforループを作成する代わりに、IEnumerable<MyObject>を受け取り、foreachループを使用するメソッドを作成します。

あなたの具体的な例:

確定的なクリーンアップを必要とするリソース(ファイルなど)の寿命を制御する。これは手で使うのは簡単ですが、リソースの所有権が転送されているときに[...スレッド間で]どのように適切に使用するのですか? C++では、共有ポインターを使用するだけで、適切なタイミングでガベージコレクションを処理します。

これをC#で(特にスレッド間で)行うことはできません。ファイルに対して何かを行う必要がある場合は、一度に1か所で行います。クラス間で渡されるアンマネージリソースを管理するラッパークラスを作成しても問題ありませんが、スレッド間でファイルを渡して別のクラスに書き込み/読み取り/クローズ/オープンを行わせないでください。アンマネージリソースの所有権を共有しないでください。クリーンアップを処理するには、Disposeパターンを使用します。

特定のジェネリックの関数をオーバーライドすることに常に苦労しています(C++での部分的なテンプレートの特殊化などが大好きです) C#で一般的なプログラミングを行う試みを放棄する必要がありますか?多分ジェネリックは意図的に制限されており、問題の特定のドメインを除いてジェネリックを使用するのはC#風ではありませんか?

C#のジェネリックはジェネリックになるように設計されています。ジェネリックの特殊化は、派生クラスで処理する必要があります。 List<T>またはList<int>の場合、List<string>の動作が異なるのはなぜですか? List<T>のすべての操作は、anyList<T>に適用できるように汎用的です。 .AddList<string>メソッドの動作を変更する場合は、派生クラスMySpecializedStringCollection : List<string>または構成済みクラスMySpecializedStringCollection : IList<string>を作成します。別の方法で。これは、リスコフの代替原則に違反したり、クラスを使用する他のユーザーを忠実に台無しにしたりすることを回避するのに役立ちます。

マクロのような機能。一般に悪い考えですが、問題の一部のドメインでは、他の回避策はありません(たとえば、デバッグリリースにのみ行くべきログの場合のような、ステートメントの条件付き評価)。それらを持たないということは、if(条件){...}ボイラープレートをさらに追加する必要があることを意味しますが、それでも副作用のトリガーという点では等しくありません。

他の人が言ったように、プリプロセッサコマンドを使用してこれを行うことができます。属性はさらに優れています。一般に、属性は、クラスの「コア」機能ではないものを処理するための最良の方法です。

つまり、C#を作成するときは、CPUとメモリではなくビジネスドメインについて完全に考える必要があることに注意してください。後で最適化できます必要な場合ですが、コードには、マッピングしようとしているビジネス原則間の関係を反映させる必要があります。

2
Stephen

私にとって最も異なったのは、新しい傾向すべてでした。オブジェクトが必要な場合は、それをルーチンに渡して基礎となるデータを共有するのを忘れて、C#パターンは自分で新しいオブジェクトを作成するだけのようです。これは、一般的に、C#の方法のようです。最初はこのパターンは非常に効率が悪いように見えましたが、C#でほとんどのオブジェクトを作成するのは簡単な操作だと思います。

ただし、静的クラスもあるので、それを使用する必要があることに気づくためだけに静的クラスを作成しようとすることがよくあります。どちらを使用するかを知るのは簡単ではありません。

C#プログラムでは、不要になったときに無視するだけで十分満足していますが、そのオブジェクトの内容を覚えておいてください。通常、「異常な」データがあるかどうかだけを考えれば、メモリのみで構成されるオブジェクトは一人で立ち去る、他の人は処分する必要があります、またはあなたはそれらを破壊する方法を見る必要があります-手動で、またはそうでなければブロックを使用して。

c#は、VBとほとんど同じなので、同じように扱うことができますRADツールVB.NETのようなC#について考えるのに役立ちます。そうすれば、構文が少しだけ異なり、VBを使用していた昔は、RAD開発)難しいのは、巨大な.netライブラリのどのビットを使用するかを決定することだけです。Googleは間違いなくあなたの友達です!

1
gbjbaanb