すべての型を定義する必要がありますか?


141

最近、コードの可読性に問題が生じました。
操作を実行し、将来の参照のためにこの操作のIDを表す文字列を返す関数がありました(WindowsのOpenFileがハンドルを返すようなものです)。ユーザーは後でこのIDを使用して、操作を開始し、その終了を監視します。

相互運用性の問題のため、IDはランダムな文字列である必要がありました。これにより、次のような非常に不明確なシグネチャを持つメソッドが作成されました。

public string CreateNewThing()

これにより、戻り型の意図が不明確になります。この文字列を、次のように意味を明確にする別の型でラップすると思いました。

public OperationIdentifier CreateNewThing()

タイプには文字列のみが含まれ、この文字列が使用されるたびに使用されます。

この操作方法の利点は、型の安全性と明確な意図であることは明らかですが、より多くのコードとあまり慣用的ではないコードも作成します。一方で、私は追加された安全性が好きですが、それはまた多くの混乱を作成します。

安全上の理由から、クラスで単純型をラップすることは良い習慣だと思いますか?


4
あなたはタイニータイプについて読むことに興味があるかもしれません。私は少し遊んでみましたが、お勧めしませんが、考えるのは楽しいアプローチです。darrenhobbs.com/2007/04/11/tiny-types
Jeroen Vannevel

40
Haskellのようなものを使用すると、型の使用がどれだけ役立つかがわかります。型は、正しいプログラムを保証するのに役立ちます。
ネイトシマー

3
Erlangのような動的言語でさえ、型システムの恩恵を受けます。そのため、動的言語であるにもかかわらずHaskellerに馴染みのあるtypespecシステムと型チェックツールがあります。C / C ++のような言語で、何かの実際の型が特定の方法を扱うためにコンパイラーに同意する整数である場合、またはクラスが型であるふりをすることを決定した場合にほとんどソートされる型を持つJavaは、言語(これは「クラス」または「タイプ」ですか?)またはタイプのあいまいさ(これは「URL」、「文字列」、または「UPC」ですか?)。最後に、できる限りのツールを使用して、明確にします。
zxq9

7
C ++に型のオーバーヘッドがあるという考えがどこから来たのかはわかりません。C ++ 11では、各ラムダに固有の型を与える際に問題が発生したコンパイラメーカーはいませんでした。しかし、TBHでは、「C / C ++型システム」に関するコメントで、2つが型システムを共有しているかのように多くの洞察を実際に見つけることは期待できません。
MSalters

2
@JDB-いいえ、ありません。似ていません
デイヴァーオドラロ

回答:


152

以下のようなプリミティブ、stringまたはint、事業ドメインでは意味を持ちません。これの直接的な結果は、製品IDが予想されるときに誤ってURLを使用したり、価格を予想するときに数量を使用したりする可能性があることです

これが、Object Calisthenicsチャレンジがプリミティブラップをルールの1つとして特徴とする理由でもあります。

ルール3:すべてのプリミティブとストリングをラップする

Java言語では、intはプリミティブであり、実際のオブジェクトではないため、オブジェクトとは異なる規則に従います。オブジェクト指向ではない構文で使用されます。さらに重要なことに、intはそれ自体が単なるスカラーであるため、意味がありません。メソッドがパラメーターとしてintを取る場合、メソッド名はインテントを表現するすべての作業を行う必要があります。同じメソッドがパラメーターとしてHourをとる場合、何が起こっているかを見るのがずっと簡単です。

同じドキュメントには、追加の利点があることが説明されています。

HourやMoneyのような小さなオブジェクトは、他のクラスの周りに散らかった振る舞いを置く明らかな場所も与えてくれます

実際、プリミティブが使用される場合、それらのタイプに関連するコードの正確な場所を追跡することは通常非常に困難であり、多くの場合、深刻なコードの重複につながりますPrice: Moneyクラスがある場合は、内部で範囲チェックを見つけるのが自然です。代わりに、製品の価格を格納intするために(さらに悪いdouble)が使用される場合、誰が範囲を検証する必要がありますか?製品?リベート?カート?

最後に、ドキュメントに記載されていない3番目の利点は、基になる型を比較的簡単に変更できることです。今日、私は場合はProductId持っているshortその基になる型として、後に、私が使用する必要があるint代わりに、チャンスはコードベース全体にまたがるません変更するためのコードです。

欠点-そして同じ議論がObject Calisthenicsエクササイズのすべてのルールに適用されます-すぐにすべてのクラスを作成するには圧倒的になりすぎるということです。場合はProduct含まれていProductPriceから継承するPositivePriceから継承するPrice順番にから継承されMoney、これがないクリーンなアーキテクチャではなく、単一のものを見つけるために、メンテナは、数十のたびにファイルを開く必要があり、完全な混乱です。

考慮すべきもう1つの点は、追加のクラスを作成するコスト(コード行数)です。ラッパーが不変である場合(通常はそうであるように)、それは、C#を使用する場合、少なくともラッパー内にある必要があることを意味します。

  • プロパティゲッター、
  • その支援分野、
  • バッキングフィールドに値を割り当てるコンストラクター、
  • カスタムToString()
  • XMLドキュメンテーションコメント(多くの行を作成ます)、
  • A EqualsおよびGetHashCodeオーバーライド(多くのLOC)。

そして最終的に、関連する場合:

  • DebuggerDisplayの属性、
  • ==and !=演算子のオーバーライド、
  • 最終的に、カプセル化された型との間でシームレスに変換する暗黙的な変換演算子のオーバーロード、
  • コードコントラクト(3つの属性を持つ、かなり長いメソッドである不変式を含む)、
  • XMLシリアル化、JSONシリアル化、またはデータベースへの/からの値の格納/ロード中に使用されるいくつかのコンバーター。

単純なラッパーの100 LOCは非常に禁止的であるため、このようなラッパーの長期的な収益性を完全に確信できる理由でもあります。ここで特に重要なのは、Thomas Junkによって説明されたスコープの概念です。ProductIdコードベース全体で使用されているものを表すために100個のLOCを記述すると、非常に便利に見えます。単一のメソッド内で3行を作成するコードに対してこのサイズのクラスを記述することは、はるかに疑わしいです。

結論:

  • (1)ミスを減らすのに役立つ、(2)コードの重複のリスクを減らす、(3)基になる型を後で変更するのに役立つ場合、アプリケーションのビジネスドメインで意味を持つクラスにプリミティブをラップします。

  • コード内で見つかったすべてのプリミティブを自動的にラップしないでください。多くの場合、使用するstringint完全に問題ありません。

実際にはpublic string CreateNewThing()、でのThingId代わりにクラスのインスタンスを返すとstring役立つ場合がありますが、次のこともできます。

  • Id<string>クラスのインスタンスを返します。これは、基になる型が文字列であることを示すジェネリック型のオブジェクトです。多くの型を維持する必要があるという欠点なしに、読みやすいという利点があります。

  • Thingクラスのインスタンスを返します。ユーザーがIDのみを必要とする場合、これは次の方法で簡単に実行できます。

    var thing = this.CreateNewThing();
    var id = thing.Id;
    

33
ここでは、基になる型を自由に変更する力が重要だと思います。今日は文字列ですが、おそらくGUIDまたは長い明日です。タイプラッパーを作成すると、その情報が非表示になり、今後の変更が少なくなります。++ここで良い答え。
ラバーダック

4
お金の場合は、通貨がMoneyオブジェクトに含まれているか、プログラム全体が同じ通貨を使用していることを確認してください(文書化する必要があります)。
パエロエベルマン

5
@Blrfl:深い継承が必要な有効なケースがありますが、通常、LOCによる強制的な支払い態度に起因する、読みやすさに注意を払わずに記述された過剰設計されたコードにこのような継承が見られます。したがって、私の答えのこの部分の否定的な性格。
Arseni Mourzenko

3
@ArTs:それは「ツールを間違った方法で使用するため、ツールを非難しましょう」という議論です。
Blrfl

6
@MainMa意味が費用のかかるエラーを回避したかもしれない場所の例:en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
cbojar

60

スコープを経験則として使用します。生成と消費のスコープが狭いほど、valuesこの値を表すオブジェクトを作成する必要が少なくなります。

次の擬似コードがあるとします

id = generateProcessId();
doFancyOtherstuff();
job.do(id);

スコープは非常に限定されており、タイプにすることには意味がありません。ただし、この値を1つのレイヤーで生成し、別のレイヤー(または別のオブジェクト)に渡す場合は、そのための型を作成するのが最適です。


14
非常に良い点。明示的な型は、オブジェクトとサービスの境界を越えて特に重要な意図伝えるための重要なツールです。
アヴナーシャハルカシュタン

+1のすべてのコードをまだdoFancyOtherstuff()サブルーチンにリファクタリングしていない場合、job.do(id)参照は単純な文字列として保持するのに十分なローカルではないと考えるかもしれません。
マークハード

これは完全に真実です!外部の世界がメソッドを使用しないことがわかっているプラ​​イベートメソッドがある場合です。その後、クラスが保護されている場合、およびクラスがパブリックであるがパブリックAPIの一部ではない場合、もう少し考えることができます。スコープスコープスコープと、多くのプリミティブ型とカスタム型の間の適切な位置にラインを配置するための多くのアート。
サミュエル

34

静的型システムは、データの不正使用を防止することについてすべてです。

これを行うタイプの明らかな例があります。

  • UUIDの月を取得できません
  • 2つの文字列を乗算することはできません。

さらに微妙な例があります

  • あなたは机の長さを使って何かにお金を払うことはできません
  • 誰かの名前をURLとして使用してHTTPリクエストを行うことはできません。

double価格と長さの両方に使用したい場合やstring、名前とURLの両方に使用したい場合があります。しかし、それを行うと、すばらしい型システムが破壊され、これらの誤用が言語の静的チェックに合格することができます。

ポンド秒をニュートン秒と混同すると、実行時の結果悪くなる可能性があります


これは特に文字列の問題です。これらは頻繁に「ユニバーサルデータタイプ」になります。

私たちは主にコンピューターとのテキストインターフェイスに慣れており、これらのヒューマンインターフェイス(UI)をプログラミングインターフェイス(API)に拡張することがよくあります。34.25を文字34.25と見なします。日付は、文字05-03-2015と見なされます。UUIDは、文字75e945ee-f1e9-11e4-b9b2-1697f925ec7bと見なされます。

しかし、このメンタルモデルはAPIの抽象化に悪影響を及ぼします。

言葉や言語は、書かれたり話されたりするので、私の思考メカニズムに何の役割も果たしていないようです。

アルバート・アインシュタイン

同様に、テキスト表現は、タイプとAPIの設計に何の役割も果たさないはずです。に注意してstringください!(および他の過度に一般的な「プリミティブ」タイプ)


タイプは「操作が意味をなすもの」を伝えます。

たとえば、私はかつてHTTP REST APIのクライアントに取り組んでいました。RESTは適切に行われ、ハイパーメディアエンティティを使用します。これには、関連するエンティティを指すハイパーリンクがあります。このクライアントでは、エンティティ(たとえば、ユーザー、アカウント、サブスクリプション)が入力されただけでなく、それらのエンティティへのリンクも入力されました(UserLink、AccountLink、SubscriptionLink)。リンクはの単なるラッパーにすぎませんでしたUriが、別のタイプではAccountLinkを使用してユーザーを取得しようとすることができませんでした。すべてが単純なものでUriあった場合、さらに悪いことに、stringこれらの間違いは実行時にのみ発見されます。

同様に、あなたの状況では、1つの目的のみに使用されるデータがありますOperation。それを他の何かに使用すべきではありません。また、Operation作成したランダムな文字列でs を識別しようとすべきではありません。別のクラスを作成すると、コードに読みやすさと安全性が追加されます。


もちろん、良いものはすべて過剰に使用できます。検討する

  1. それがあなたのコードにどれだけ明確になるか

  2. 使用頻度

(抽象的な意味での)データの「タイプ」が、明確な目的のために、およびコードインターフェイス間で頻繁に使用される場合、冗長性を犠牲にして、別個のクラスになる非常に良い候補です。


21
「文字列はしばしば「普遍的な」データ型になります」-それは、「舌で型付けされた言語」モニカーの舌の起源です。
–MSalters

@MSalters、ハハ、とてもいい。
ポールドレーパー

4
そしてもちろん、日付として解釈されるとき、何が05-03-2015 意味 するのでしょうか?
CVn

10

安全上の理由から、クラスで単純型をラップすることは良い習慣だと思いますか?

時々。

これはstring、より具体的なものの代わりにを使用することから生じるかもしれない問題を重くする必要があるそれらのケースの1つですOperationIdentifier。彼らの重症度は何ですか?彼らの可能性は何ですか?

次に、他のタイプを使用するコストを考慮する必要があります。使用するのはどれほど苦痛ですか?どれくらいの仕事が必要ですか?

場合によっては、適切な具体的な型を使用することで時間と労力を節約できます。他では、それはトラブルの価値がないでしょう。

一般的に、この種のことは今日よりも多くすべきだと思います。ドメイン内に何かを意味するエンティティがある場合、そのエンティティはビジネスとともに変化/成長する可能性が高いため、それを独自のタイプとして持つことをお勧めします。


10

私は一般的に、プリミティブ型と文字列の型を作成する必要があることが多いことに同意しますが、上記の回答ではほとんどの場合型を作成することをお勧めしているため、次の理由と理由を挙げます:

  • パフォーマンス。ここで実際の言語を参照する必要があります。C#では、クライアントIDが短く、クラスにラップする場合、64ビットシステムでは8バイトになり、ヒープに割り当てられるようになったため、多くのオーバーヘッド:メモリが作成されました。
  • タイプを仮定するとき。クライアントIDが短く、何らかの方法でそれをパックするロジックがある場合-通常、タイプについてはすでに推測しています。これらすべての場所で、抽象化を破らなければなりません。それが1つのスポットである場合、それは大したことではありません、それがいたる所にある場合、あなたはプリミティブを使用している半分の時間を見つけるかもしれません。
  • すべての言語にtypedefがあるわけではないからです。そして、そうではない言語と既に書かれているコードの場合、そのような変更を行うことはバグを引き起こす可能性さえある大きなタスクになる可能性があります(しかし、誰もが良いカバレッジのテストスイートを持っていますよね?)。
  • 場合によっては読みやすさが低下します。これを印刷するにはどうすればよいですか?これをどのように検証しますか?nullをチェックする必要がありますか?タイプの定義にドリルする必要があるすべての質問です。

3
ラッパー型がクラスではなく構造体である場合、short(たとえば)ラップまたはラップ解除されるコストは同じです。(?)
MathematicalOrchid

1
型が独自の検証をカプセル化するのは良い設計ではないでしょうか?または、それを検証するヘルパータイプを作成するには?
ニックウデル

1
不必要にパッキングまたは変更することはアンチパターンです。変換が明示的かつ真に必要な場合を除き、情報はアプリケーションコードを透過的に流れる必要があります。タイプは、システム全体に散らばっている大量の貧弱な実装の代わりに、通常、単一の場所にある少量の良いコードを代用するので、良いです。
トーマスW

7

いいえ、「すべて」のタイプ(クラス)を定義しないでください。

しかし、他の答えが述べているように、そうすることしばしば有用です。コードを作成、テスト、および保守する際に、適切な型またはクラスがないために、可能であれば意識的に過度に摩擦の感覚または感覚を発達させる必要があります。私にとって、あまりにも多くの摩擦の始まりは、複数のプリミティブ値を単一の値として統合したいとき、または値を検証する必要があるときです(つまり、プリミティブタイプのすべての可能な値が「暗黙のタイプ」)。

私があなたの質問で提起したものなどの考慮事項が、私のコードの設計が多すぎることに責任があることがわかりました。私は、必要以上のコードを書くことを意図的に避ける習慣を身につけました。コードの適切な(自動化された)テストを書いていることを願っています。もしそうなら、コードのリファクタリングとタイプまたはクラスの追加を簡単に行うことができます

Telastynの答えThomas Junkの答えはどちらも、関連するコードのコンテキストと使用法について非常に良い点を示しています。コードの単一ブロック(メソッド、ループ、usingブロックなど)内で値を使用している場合は、プリミティブ型を使用するだけで問題ありません。値のセットを繰り返し使用する場合や、他の多くの場所でプリミティブ型を使用することもできます。ただし、値のセットを使用する頻度が高く、広く、その値のセットがプリミティブ型で表される値に対応しているほど、クラスまたは型に値をカプセル化することを検討する必要があります。


5

表面上は、操作を特定するだけでよいように見えます。

さらに、操作で何をするべきかを言います:

操作開始する [および] 終了監視する

これは「識別の使用方法」であるかのように言いますが、これらは操作を説明するプロパティです。それは型定義のように思えますが、非常によく適合する「コマンドパターン」と呼ばれるパターンさえあります。

それは言います

コマンドパターン[...]は、後でアクション実行したりイベントをトリガーしたりするために必要なすべての情報をカプセル化するために使用されます。

これは、あなたがあなたの業務でやりたいことと比較して非常に似ていると思います。(両方の引用符で太字にしたフレーズを比較してください)文字列を返す代わりにOperation、たとえばoopでそのクラスのオブジェクトへのポインタなど、抽象的な意味での識別子を返します。

コメントについて

このクラスをコマンドと呼び、その中にロジックを持たないのはwtf-yです。

いいえ、そうではありません。ことを覚えておいてくださいパターンは非常に抽象的で、彼らはややメタであるという事実で非常に抽象的。つまり、実際の概念ではなく、プログラミング自体を抽象化することがよくあります。コマンドパターンは、関数呼び出しの抽象化です。(またはメソッド)パラメータの値を渡した直後、実行の直前に誰かが一時停止ボタンを押すように、後で再開します。

以下はoopを検討していますが、その背後にある動機はあらゆるパラダイムに当てはまるはずです。ロジックをコマンドに配置することが悪いことだと考えられる理由をいくつか説明します。

  1. コマンドにすべてのロジックが含まれていると、肥大化して煩雑になります。「まあ、この機能はすべて別のクラスにあるべきだ」と考えるでしょう。コマンドからロジックをリファクタリングします。
  2. コマンドにすべてのロジックが含まれている場合、テストするときにテストして邪魔になるのは難しいでしょう。「なんてこった、このコマンドをナンセンスに使用しなければならないのか。この関数が1を吐き出すかどうかだけをテストしたい。後で呼び出したくないので、今すぐテストしたい!」
  3. コマンドにすべてのロジックが含まれている場合、終了時に報告するのはあまり意味がありません。デフォルトで同期的に実行される関数について考える場合、実行が終了したときに通知されることは意味がありません。あなたの操作は実際にアバターをシネマ形式にレンダリングするために別のスレッドを開始しているかもしれません(ラズベリーパイに追加のスレッドが必要な場合があります)、サーバーからデータを取得する必要があるかもしれません...終了時にレポートを返すのは、何らかの非同期性(それは言葉ですか?)が進行しているためかもしれません。スレッドを実行したり、サーバーに接続したりすることは、コマンドに含めるべきではないロジックだと思います。これはややメタ議論であり、おそらく議論の余地がある。意味がないと思われる場合は、コメントで教えてください。

要約すると、コマンドパターンを使用すると、機能をオブジェクトにラップして、後で実行できます。モジュール性(機能はコマンドを介して実行されるかどうかに関係なく存在する)、テスト可能性(機能はコマンドなしでテスト可能である必要があります)、および本質的に良いコードを書くための要求を表現する他のすべてのバズワードのために、実際のロジックは入れませんコマンドに。


パターンは抽象的であるため、現実世界の優れた隠goodを思い付くのは困難です。これが試みです:

「おばあちゃん、12時のテレビの録画ボタンを押してください。チャンネル1のシンプソンズをお見逃しなく。」

おばあちゃんは、レコードボタンを押したときに技術的に何が起こるかわかりません。ロジックは他の場所(TV内)にあります。そしてそれは良いことです。機能はカプセル化され、情報はコマンドから隠されます。それはAPIのユーザーであり、必ずしもロジックの一部ではありません。


あなたは正しいです、私はそのようにそれについて考えていません。ただし、操作のロジックは別のクラスにカプセル化されており、コマンドであるオブジェクト内に配置できないため、コマンドパターンは100%適合していません。このクラスをコマンドと呼び、その中にロジックを持たないのはwtf-yです。
ジブ

これは非常に理にかなっています。OperationIdentifierではなく、Operationを返すことを検討してください。これは、タスクまたはTask <T>を返すC#TPLに似ています。@Ziv:「操作のロジックは別のクラスにカプセル化されています」と言います。しかし、それはここからも提供できないということですか?
Mobyディスク

@Ziv私は違うことを請います。私が私の答えに加えた理由を見て、それらがあなたにとって意味があるかどうか見てください。=)
null

5

プリミティブ型のラップの背後にあるアイデア、

  • ドメイン固有の言語を確立するには
  • 誤った値を渡すことにより、ユーザーが犯す間違いを制限する

明らかに、どこでもそれを行うことは非常に難しく、実用的ではありませんが、必要に応じて型をラップすることが重要です。

たとえば、Orderクラスがある場合、

Order
{
   long OrderId { get; set; }
   string InvoiceNumber { get; set; }
   string Description { get; set; }
   double Amount { get; set; }
   string CurrencyCode { get; set; }
}

Orderを検索するための重要なプロパティは、主にOrderIdとInvoiceNumberです。また、AmountとCurrencyCodeは密接に関連しています。Amountを変更せずにCurrencyCodeを変更すると、注文は有効と見なされなくなります。

したがって、このシナリオではOrderId、InvoiceNumberをラッピングし、通貨のコンポジットを導入するだけが理にかなっており、おそらく記述をラッピングすることは意味がありません。望ましい結果は次のようになります。

    Order
    {
       OrderIdType OrderId { get; set; }
       InvoiceNumberType InvoiceNumber { get; set; }
       string Description { get; set; }
       Currency Amount { get; set; }
    }

したがって、すべてをラップする理由はありませんが、実際に重要なことです。


4

不評な意見:

通常、新しいタイプを定義しないでください!

プリミティブクラスまたは基本クラスをラップするための新しい型の定義は、疑似型の定義と呼ばれることもあります。IBMは、これをここでは悪い慣行あると説明しています(この場合、ジェネリックとの誤用に特に焦点を当てています)。

疑似型は、一般的なライブラリ機能を役に立たなくします。

Javaの数学関数は、すべての数のプリミティブで機能します。しかし、新しいクラスのPercentage(0〜1の範囲にある可能性があるdoubleをラップする)を定義する場合、これらの関数はすべて役に立たず、Percentageクラスの内部表現を知る必要がある(さらに悪い)クラスによってラップする必要があります。

擬似タイプがバイラルに

複数のライブラリを作成する場合、これらの疑似タイプがバイラルになることがよくあります。1つのライブラリで上記のPercentageクラスを使用する場合、ライブラリの境界で変換する必要があります(すべての安全性/意味/論理/そのタイプを作成するために持っていたその他の理由を失う)、またはこれらのクラスを作成する必要があります他のライブラリにもアクセスできます。単純なdoubleで十分なタイプの新しいライブラリに感染します。

メッセージを奪う

ラップする型が多くのビジネスロジックを必要としない限り、疑似クラスでラップすることをお勧めします。深刻なビジネス上の制約がある場合にのみ、クラスをラップする必要があります。それ以外の場合、変数に正しく名前を付けると、意味を伝えるのに大いに役立ちます。

例:

uint完全にユーザーIDを表すことができ、我々はのための演算子で構築されたJavaのを使用し続けることができるuint(例えば==など)■私たちは、ユーザIDの「内部状態」を保護するためにビジネスロジックを必要としません。


同意する。すべてのプログラミング哲学は結構ですが、実際には、あなたはそれが同様に動作させるために持っている
ユアン・

英国で最も貧しい人々への融資の広告を見た場合、範囲が
0..1の

1
C / C ++ / Objective Cでは、typedefを使用できます。これにより、既存のプリミティブ型の新しい名前が与えられます。一方、たとえば、OrderIdをuint32_tからuint64_tに変更した場合(40億を超える注文を処理する必要があるため)、基になる型に関係なくコードは機能するはずです。
gnasher729

私はあなたの勇気のためにあなたに投票しませんが、疑似タイプとトップレートの答えで定義されたタイプは異なります。これらはビジネス固有のタイプです。疑似型は、まだ一般的な型です。
0fnt

@ gnasher729:C ++ 11には、ラッピングプロセスをユーザーにとって非常に便利にするユーザー定義リテラルもあります。距離d = 36.0_mi + 42.0_km; msdn.microsoft.com/en-us/library/dn919277.aspx
damix911

3

すべてのヒントと同様に、ルールをいつ適用するかを知ることがスキルです。型駆動型言語で独自の型を作成すると、型チェックが行われます。だから、一般的にそれは良い計画になるだろう。

NamingConventionは読みやすさの鍵です。組み合わされた2つは、意図を明確に伝えることができます。
しかし、///はまだ便利です。

そう、私は彼らの寿命がクラスの境界を超えているときに多くの独自の型を作成すると言うでしょう。また、常にClassではなく、Structとの両方の使用を検討してくださいClass


2
どういう///意味ですか?
ゲイブ

1
///は、インテリセンスが呼び出しメソッドポイントで署名のドキュメントを表示できるようにするC#のドキュメントコメントです。コードを文書化する最良の方法を検討してください。ツールによって養殖でき、リーチインテリジェンス動作を提供できます。msdn.microsoft.com/en-us/library/tkxs89c5%28v=vs.71%29.aspx
phil soady
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.