コード生成またはT4テンプレート、彼らは本当に悪ですか?
-
22-08-2019 - |
質問
私は聞いたことがある人は、コード生成とT4テンプレートを使用すべきではないと述べています。その背後にあるロジックを使用すると、発電機を使用してコードを生成しているならば、ジェネリックとテンプレートによるコードをビルドするためのより良い、より効率的な方法があるということです。
私は少し上のこの声明に同意しながら、、私は本当に自分自身をインスタンス化インスタンスの言うことができるテンプレートを構築するための効果的な方法を発見していません。言い換えれば、私は決してしないことができます:
return new T();
私はデータベースの値に基づいてコードを生成したい場合は、また、私はT4テンプレートと一緒にMicrosoft.SqlServer.Management.SMO
を使用すると、ReSharperのをコピー/ペーストまたは使用せずに、コードの質量量を生成するのに素晴らしいされていることを発見した。
私はジェネリックで発見した問題の多くは、あまりにも私のショックにそれらを理解していない開発者が多いということです。私は解決策のためにジェネリックを調べる行うと、C#は、あなたが私の心の中で論理的に見えるかもしれませんが何かを行うことができないと述べているので、それが複雑になる時間があります。
あなたの考えは何ですか?あなたは発電機を構築することを好むか、ジェネリックを使用することを好むのですか?また、どこまでジェネリックは行くことができますか?私は、ジェネリック医薬品についてのまともな量を知っているが、T4テンプレートに頼る私はいつも私の原因ことに遭遇するトラップと落とし穴があります。
あなたは柔軟性を大量に必要とするシナリオを処理するために、より適切な方法は何ですか?ああ、この質問へのボーナスとして、C#およびジェネリックに良いリソースは何ですか?
解決
あなたは新しいTを(行うことができます)。あなたがこれを行う場合は、
public class Meh<T>
where T : new()
{
public static T CreateOne()
{
return new T();
}
}
コードジェネレータとして。私は何の問題もなく毎日いずれかを使用します。私は実際に今1を使用しています: - )
ジェネリックは、コードジェネレータは、別の解決、一つの問題を解決します。例えば、UMLエディタを使用してビジネス・モデルを作成し、その後、私はを使用して、時間のすべてがそうであるように永続化コードを使用してクラスを生成します各永続クラスは完全に異なっているので、このツールには、ジェネリックで達成することができませんでした。
ジェネリック医薬品の良いソース用として。最高のはもちろんのジョンスキートの本のに持っています! : - )
他のヒント
T4の創始者として、私はこの質問を守るためにあなたが想像できるようかなりの数回を持っていた: - )
私の信念は、その最高のコード生成で再利用可能なライブラリを使用して同等の値を生成する方法のステップがあるということです。
多くの人が言ったように、、DRYを維持するための重要な概念は、これまで手動で生成されたコードを変更するのではなく、ソースメタデータの変更か、コードジェネレータのバグを見つけたときに再生する能力を維持する、ことはありません。その時点で生成されたコードは、オブジェクトコードの特性の多くを持って、あなたは、コピー/ペーストタイプの問題に実行しないでください。
一般的には、それが正しく同じレベルにまで使用コストを取得し、高品質の基本ライブラリを操作することであるよりも、(特にテンプレートベースのシステムとの)パラメータ化コードジェネレータを生成するために、はるかに少ない労力だので、それは迅速です道の整合性から値を取得し、繰り返しエラーを削除します。
しかし、私はまだ完成システムは、ほとんどの場合、より少ない総コードを持つことによって改善されるだろうと信じています。何もない場合は(人々は、彼らが最も確かではありません。この点で自由コストなどのジェネリック医薬品、と考える傾向があるが)、そのメモリフットプリントは、ほとんど常に大幅に小さくなります。
あなたは、コードジェネレータを使用して、いくつかの値を実現した場合、、これは多くの場合、あなたに生成されたコードベースからライブラリを収穫に投資するいくつかの時間やお金や営業権を購入します。その後、インクリメンタルに新しいライブラリを対象とし、うまくいけばはるかに少ないコードを生成するコードジェネレータを再設計することができます。すすぎと繰り返しています。
私に作られ、それがこのスレッドにアップしていますされている一つの興味深い対位法は豊かな、複雑な、パラメトリックライブラリは特に深くプラットフォームに浸されていないもののために、学習曲線の面で最も簡単なものではありませんということです。シンプルな基本的なフレームワーク上にコード生成に固執することは冗長なコードを生成することができますが、それはしばしば非常にシンプルで読みやすいことができます。
もちろん、あなたは発電機の分散の多くと非常に豊富なパラメータを持っている場合は、あなたは自分のテンプレート内の複雑さのために複雑ANあなたの製品をトレードオフされる可能性があります。これはにスライドすると頭痛の同じくらいのメンテナンスを行うことができます簡単な道である - 。そのために気を付ける
の生成コードは悪ではない、それは香りがしません!キーは、適切なタイミングで適切なコードを生成することです。私はたまにしか、それを使用しますが、私はときに非常に便利です - 私は、T4は素晴らしいと思います。コードを生成することは悪いことを、無条件に、言うことは無条件に夢中です!
これはコード・ジェネレータは限りコードの生成は、通常のビルドプロセスの一部ではなく、あなたが一度実行し、その出力を保つものですと罰金です私には思えます。一度だけコードジェネレータを使用して、それを作成したデータを破棄した場合、あなただけ自動的に大規模なDRY違反とメンテナンス頭痛を作成しているので、私はこの警告を追加します。コードを生成するのに対し、すべての時間を有効あなたが生成を行うために使用しているものは何でもすると、実際のソースコードで、生成されたファイルは、あなたがほとんど無視すべきちょうど中間のコンパイルの段階であることを意味します。
Lexとyaccはあなたが効率的な方法で機能を指定し、そこから効率的なコードを生成することを可能にするのツールの古典的な例です。手で自分の仕事をしようとすると、あなたの開発時間を長くし、おそらくあまり効率的で、より少ない読み取り可能なコードが生成されます。あなたは確かに直接あなたのコードにlexとyaccのようなものを組み込むと、実行時に代わりのコンパイル時に自分の仕事を行うことができながら、それは確かにあなたのコードにかなりの複雑さを追加し、それを遅くします。あなたが実際に実行時に仕様を変更する必要がある場合、それはそれの価値があるが、ほとんどの通常のケースでは、コンパイル時にあなたのためのコードを生成するためのlex / yaccのを使用することは大きな勝利であるかもしれません。
のVisual Studio 2010にあるものの良い割合は、コード生成なしには不可能であろう。 Entity Frameworkのは不可能であろう。フォームにコントロールをドラッグ&ドロップの簡単な行為はできず、またLINQのでしょうではないでしょう。そう多くはそれについて何も考えずにそれを使用するように、そのコード生成を使用すべきではないと言うことは奇妙である。
多分それは少し厳しいですが、私のためのコード生成が臭いです。
使用されていることを、コード生成は、「自分自身を繰り返してはいけない」形で表現することができる数多くの基礎となる共通の原則があることを意味します。それは少し時間がかかるかもしれませんが、あなたはメカニックが含まれているインフラストラクチャに基づいてのみ、実際に変更するビットを含むクラスで終わるとき、それは満足です。
ジェネリック医薬品については...いいえ、私はそれであまりにも多くの問題を持っていません。現在は動作しない唯一のものは、
と言っていますList<Animal> a = new List<Animal>();
List<object> o = a;
しかし、たとえそれは、C#の次のバージョンで可能になります。
より多くのコードでは、より多くの複雑さを意味します。より多くの複雑さは、今度はプロジェクト全体のコストが高いことを意味長い修正サイクルを、意味し、非表示にするには、バグのためのより多くの場所を意味します。
可能であれば、私は、同等の機能を提供するために、コードの量を最小限にすることを好みます。理想的には、動的(プログラム)を使用することなく、コード生成よりも近づきます。反射、属性、態様、およびジェネリック医薬品は、最後の手段として世代を残して、DRY戦略のための多くのオプションを提供します。
コード生成が私のために彼らは、それ自体で悪ではないなどの言語、フレームワーク、で見つかった多くの問題の回避策ですが、私は言語(C#の)とフレームワークを解放するために、それは非常に非常に悪いです(つまり、悪を)言うことになりますコピー&ペーストする力(性質上のスワップは、イベントがマクロの欠如、トリガ)または(結合WPF)魔法の数字を使用します。
だから、私は泣いたが、私が持っているので、私は、それらを使用します。
私はまた、コード生成のためのT4とジェネリック医薬品を使用しました。どちらも優れている彼らの長所と短所を持っており、異なる目的に適しています。
私の場合、私は、データベーススキーマに基づくエンティティ、DALとBLLを生成するために、T4を使用しています。しかし、DALとBLLはジェネリックとリフレクションに基づいて、私が建てミニORMを参照します。だから私は、あなたがいる限り、あなたがコントロールしておくと、小型でシンプルな、それを維持するよう、それらを並べて使用することができると思います。
ジェネリックが動的であるT4は、静的コードを生成します。あなたがジェネリックを使用する場合は、「ハードコード化された」ソリューションよりもパフォーマンスが低いと言われてリフレクションを使用しています。もちろん、反射結果をキャッシュすることができます。
:;については、 "新しいT()を返す"、私はこのような動的メソッドを使用します
public class ObjectCreateMethod
{
delegate object MethodInvoker();
MethodInvoker methodHandler = null;
public ObjectCreateMethod(Type type)
{
CreateMethod(type.GetConstructor(Type.EmptyTypes));
}
public ObjectCreateMethod(ConstructorInfo target)
{
CreateMethod(target);
}
void CreateMethod(ConstructorInfo target)
{
DynamicMethod dynamic = new DynamicMethod(string.Empty,
typeof(object),
new Type[0],
target.DeclaringType);
ILGenerator il = dynamic.GetILGenerator();
il.DeclareLocal(target.DeclaringType);
il.Emit(OpCodes.Newobj, target);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
}
public object CreateInstance()
{
return methodHandler();
}
}
そこで、私はこのようにそれを呼び出します
ObjectCreateMethod _MetodoDinamico = new ObjectCreateMethod(info.PropertyType);
object _nuevaEntidad = _MetodoDinamico.CreateInstance();
ジェネリック医薬品とコード生成は、二つの異なるものです。いくつかのケースでは、代わりに、コード生成のジェネリックを使用することができ、それらのために私は、あなたがしなければならないと考えています。他の例のコードを生成するための強力なツールです。
あなたは、単にいくつかのデータ入力に基づいてコードを生成するために必要なすべてのケースでは、コード生成が移動するための方法です。最も明白な、しかし決して唯一の例では、Visual Studioでのフォームエディタです。ここでの入力は、デザイナーのデータであり、出力はコードです。この場合、ジェネリック医薬品は本当にまったく何の助けではありませんが、VSは単にGUIレイアウトに基づいてコードを生成することは非常にいいです。
コード・ジェネレータは、ターゲットのlangaugeの欠陥や機能の欠如を示すコードのにおいを考えることができます。
たとえば、「一般化することはできない持続オブジェクトは」、「自動的にデータを永続化C#でのオブジェクトは、C#で一般化することはできない」と考える方が良いだろうと、ここで言われているが、理由は私は確かに様々な方法を使用してPythonですることができます。
Pythonのアプローチは、しかしながら、いずれかの要件に応じて、ファンクタまたは文字列を返す演算子[](文字列としてMETHOD_NAME)の使用を介して静的言語でエミュレートすることができます。残念ながら、その解決策が常に適用されない、とファンクタを返すことは不便なことができます。
私が作っていますポイントは、コードジェネレータは、手元にある特定の問題のために、より便利に、の専門の構文を提供することによって対処され、選択した言語で欠陥を示している。
コピー/ペーストタイプ(オームズが作るような)にも非常に便利です...
あなたのデータベースを作成することができ、その後、ORMは、お好みの言語で表現、そのデータベースの定義のコピーを生成した。
利点は、(あなたは良いものを持っている場合)することができます定義のコピーを再生成し、あなたの元の定義(データベース)、プレスコンパイルおよびORMを変更したときに付属しています。今すぐあなたのデータベースへのすべての参照は、コンパイラの型検査によって確認することができ、あなたのコードは、あなたがもう存在しないテーブルやカラムを使用しているとき、コンパイルに失敗します。
これについて考える:私は私のコードでメソッドを数回呼び出す場合、私はもともと、このメソッドに与えた名前に言及していないのですか?私は何度もその名を繰り返し続ける...言語設計者はこの問題を認識し、解決策として、「タイプ・安全」を思い付きました。 (DRYが示唆するように、私たちが何をすべき)のコピーを削除し、代わりに正しさのためにそれらをチェックしません。
テーブル名と列名を参照する場合、ORM生成されたコードは、同じソリューションをもたらします。コピー/参照を削除していますが、代わりにクラスやプロパティを参照することができます(タイプセーフな)言語にデータベースの定義を持っていません。あなたが古くなったりスペルミステーブル(クラス)または列(プロパティ)を参照するとき保証コンパイル時のエラーの代わりに、実行時のもの。
:一緒コンパイラの型チェックで、これは同様の方法で同様の問題を解決します。引用: 私は本当に自分自身をインスタンス化インスタンスの言うことができるテンプレートを構築するための効果的な方法を発見していません。言い換えれば、私は決してしないことができます:
;新しいT()を返します
public abstract class MehBase<TSelf, TParam1, TParam2>
where TSelf : MehBase<TSelf, TParam1, TParam2>, new()
{
public static TSelf CreateOne()
{
return new TSelf();
}
}
public class Meh<TParam1, TParam2> : MehBase<Meh<TParam1, TParam2>, TParam1, TParam2>
{
public void Proof()
{
Meh<TParam1, TParam2> instanceOfSelf1 = Meh<TParam1, TParam2>.CreateOne();
Meh<int, string> instanceOfSelf2 = Meh<int, string>.CreateOne();
}
}
コード生成は、ジェネリック医薬品、テンプレート、および他のそのようなショートカットのように、強力なツールです。そして、最も強力なツールと同様に、それが良いためと悪のためにそのユーザーのcapailityを増幅 - 。彼らは分離することはできません。
あなたが徹底的に自分のコードジェネレータを理解していれば、それが生成されます、すべてを予想するので、なぜ、そしてそれを持って、その後、正当な理由のためにそうすることを意図します。しかし、あなたは、あなたが向かっている場所がわからないようにしているところを過ぎてあなたを取得すること(または他の技術のいずれか)を使用していない、またはそこに取得する方法。
一部の人々は、あなたの現在の問題を解決しますと、いくつかの動作が実現した場合、あなたは黄金だ、と思います。それあなたが次の開発者のためのあなたの道に残してどれだけ嫌なものと不透明必ずしも明確ではありません(自分自身かもしれません。)
なぜ、本当に、本当に速い/ペーストをコピーすることができるというん、それはもはや許容作る?
それは私が見ることができるコード生成のための唯一の正当化だ。
発電機は、あなたが必要なすべての柔軟性を提供している場合でも、、あなたはまだその柔軟性使用する方法を学習する必要があります - 。まだ必要な学習とテストの別の層である
そして、それはゼロ時間に実行されている場合でも、それはまだコードをbloatsます。
私は自分のデータアクセスクラスを巻か。それはなど、接続、トランザクション、ストアドプロシージャのPARMSなどについてのすべてを知っている、と私は一度だけ、すべてのADO.NETのものを書かなければなりませんでした。
これは、今では長い間、私はハード構文ぶっきらぼうを覚えているために押されるだろうと、その中の接続オブジェクトで何かを書く(あるいは見)しなければならなかったので、されています。