List <T>から継承してみませんか?

1459
Superbest 2014-02-11 17:01.

プログラムを計画するとき、私はしばしば次のような一連の考えから始めます。

サッカーチームは、サッカー選手の単なるリストです。したがって、私はそれを次のように表す必要があります。

var football_team = new List<FootballPlayer>();

このリストの順序は、プレーヤーが名簿にリストされている順序を表しています。

しかし、後で、チームには、単なるプレーヤーのリスト以外に、記録する必要のある他のプロパティもあることに気付きました。たとえば、今シーズンのスコアの現在の合計、現在の予算、均一な色、stringチームの名前を表すなどです。

だから私は思う:

さて、サッカーチームは選手のリストのようなものですが、それに加えて、名前(a string)と現在のスコアの合計(an int)があります。.NETにはサッカーチームを格納するためのクラスがないので、自分でクラスを作成します。最も類似していて関連性のある既存の構造はList<FootballPlayer>ですので、それを継承します。

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

しかし、ガイドラインでは、から継承すべきではないと書かList<T>れていることがわかりました。私はこのガイドラインに2つの点で完全に混乱しています。

何故なの?

どうやらListパフォーマンスのために最適化されています。どうして?拡張すると、どのようなパフォーマンスの問題が発生しListますか?正確には何が壊れますか?

私が見たもう1つの理由Listは、Microsoftによって提供されており、私はそれを制御リストからの継承<T>ためリストからの継承<T>。しかし、私はこれを理解するのに苦労しています。パブリックAPIとは何ですか?なぜ気にする必要がありますか?現在のプロジェクトにこのパブリックAPIがない、またはない可能性がある場合、このガイドラインを安全に無視できますか?私は継承なければList 、それは私が公共のAPIを必要と判明し、どのような困難私が持っているのだろうか?

なぜそれが重要なのですか?リストはリストです。何が変わる可能性がありますか?何を変更したいですか?

そして最後に、Microsoftが私に継承を望まなかったのならList、なぜ彼らはクラスを作らなかったのsealedですか?

他に何を使うべきですか?

どうやら、カスタムコレクションのために、MicrosoftCollectionはの代わりに拡張されるべきクラスを提供しましたList。しかし、このクラスは非常にむき出しであり、コレクションへのAddRangeコレクションへのAddRange有用なものは多くありません。jvitor83の答えは、その特定の方法のパフォーマンスの理論的根拠を提供しますが、遅いのAddRangeはどうしてないよりも良くないのAddRangeでしょうか。

からのCollection継承はList、からの継承よりもはるかに多くの作業であり、メリットはありません。確かにマイクロソフトは理由もなく余分な仕事をするように私に言わないので、私は何かを誤解しているように感じずにはいられません、そして継承Collectionは実際には私の問題の正しい解決策ではありません。

の実装などの提案を見てきましたIList。いいえ。これは数十行の定型コードであり、何も得られません。

最後に、List何かでラップすることを提案する人もいます。

class FootballTeam 
{ 
    public List<FootballPlayer> Players; 
}

これには2つの問題があります。

  1. それは私のコードを不必要に冗長にします。my_team.Players.Countだけでなく、今すぐ電話する必要がありますmy_team.Count。ありがたいことに、C#を使用すると、インデクサーを定義してインデックスを透過的にし、内部のすべてのメソッドを転送できますList...しかし、それは多くのコードです!そのすべての作業で何が得られますか?

  2. 単純なことでは意味がありません。サッカーチームは選手のリストを「持っていません」。これは、ある選手のリスト。「JohnMcFootballerがSomeTeamのプレーヤーに加わった」とは言わないでください。あなたは「ジョンがSomeTeamに参加しました」と言います。「文字列の文字」に文字を追加するのではなく、文字列に文字を追加します。図書館の本に本を追加するのではなく、図書館に本を追加します。

「内部」で起こっていることは「XをYの内部リストに追加する」と言えることは理解していますが、これは非常に直感に反する世界の考え方のようです。

私の質問(要約)

データ構造を表す正しいC#の方法は何ですか。これは、「論理的に」(つまり、「人間の心に」)、いくつかのベルとホイッスルlistthings付いたものです。

からの継承はList<T>常に受け入れられませんか?いつ受け入れられますか?なぜ/なぜそうではないのですか?継承するかどうかを決定するとき、プログラマーは何を考慮しなければなりませList<T>んか?

27 answers

1625
Eric Lippert 2014-02-11 19:43.

ここにいくつかの良い答えがあります。以下の点を付け加えておきます。

データ構造を表す正しいC#の方法は何ですか。これは、「論理的に」(つまり、「人間の心に」)、いくつかのベルとホイッスルを備えたもののリストにすぎません。

サッカーの存在に精通しているコンピュータープログラマー以外の10人に、空欄に記入してもらいます。

サッカーチームは特定の種類の_____です

誰かが「いくつかの鐘と笛を持ったサッカー選手のリスト」と言いました、それとも全員が「スポーツチーム」または「クラブ」または「組織」と言いましたか?サッカーチームが特定の種類の選手のリストであるというあなたの考えは、あなたの人間の心とあなたの人間の心だけにあります。

List<T>あるメカニズム。フットボールチームはビジネスオブジェクトです。つまり、プログラムのビジネスドメインにある概念を表すオブジェクトです。それらを混ぜないでください!サッカーチームは一種のチームです。それ名簿を持ってます、名簿はプレーヤーのリストです。名簿は特定の種類のプレーヤーのリストではありません。名簿選手のリストです。したがって、Rosterそれと呼ばれるプロパティを作成しますList<Player>。そしてReadOnlyList<Player>、サッカーチームについて知っているすべての人が名簿から選手を削除できると信じていない限り、あなたがそれにいる間にそれを作ってください。

からの継承はList<T>常に受け入れられませんか?

誰に受け入れられない?私?番号。

いつ受け入れられますか?

あなたはメカニズム構築している場合は拡張List<T>メカニズムを

継承するかどうかを決定するとき、プログラマーは何を考慮しなければなりませList<T>んか?

メカニズムまたはビジネスオブジェクトを構築していますか?

しかし、それはたくさんのコードです!そのすべての作業で何が得られますか?

あなたはList<T>50回以上の関連メンバーのための転送方法を書くのにあなたがかかったであろうあなたの質問をタイプすることにもっと時間を費やしました。あなたは明らかに冗長性を恐れていません、そして私たちはここで非常に少量のコードについて話している。これは数分の作業です。

更新

もう少し考えてみましたが、サッカーチームを選手のリストとしてモデル化しないもう1つの理由があります。実際、サッカーチームを選手のリストも持っているものとしてモデル化するのは悪い考えかもしれません。プレーヤーのリストを持っている/持っているチームの問題は、あなたが持っているのはある瞬間のチームのスナップショットであるということです。このクラスのビジネスケースはわかりませんが、サッカーチームを代表するクラスがある場合は、「2003年から2013年の間に怪我のために試合に出場できなかったシーホークスの選手は何人いますか」などの質問をしたいと思います。または「以前に別のチームでプレーしたデンバーのどの選手が、ヤードの前年比で最大の増加を示しましたか?」または「ピガーズは今年ずっと行きましたか?

つまり、サッカーチームは、プレーヤーが採用されたとき、負傷したとき、引退したときなどの歴史的事実のコレクションとして適切にモデル化されいるように見えます。センターですが、より歴史的な視点を必要とする、このオブジェクトでやりたい他の興味深いことがあるかもしれません。

169
myermian 2014-02-11 17:26.

うわー、あなたの投稿にはたくさんの質問とポイントがあります。あなたがマイクロソフトから得る推論のほとんどは正確に正しいです。についてのすべてから始めましょうList<T>

  • List<T> され、高度に最適化されました。その主な用途は、オブジェクトのプライベートメンバーとして使用されることです。
  • より親しみやすい名前のクラスを作成したい場合があるため、Microsoftはそれを封印しませんでしたclass MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... }。今ではそれを行うのと同じくらい簡単var list = new MyList<int, string>();です。
  • CA1002:一般的なリストを公開しない:基本的に、このアプリを唯一の開発者として使用することを計画している場合でも、優れたコーディング手法で開発する価値があるため、それらはあなたと第二の性質に浸透します。IList<T>インデックス付きリストを持つコンシューマーが必要な場合は、リストを公開することができます。これにより、後でクラス内の実装を変更できます。
  • マイクロソフトCollection<T>は、それが一般的な概念であるため、非常に一般的なものにしました...名前がすべてを物語っています。それは単なるコレクションです。などのより正確なバージョンがありSortedCollection<T>ObservableCollection<T>ReadOnlyCollection<T>実装各々は、等IList<T>しかし List<T>
  • Collection<T>メンバー(つまり、追加、削除など)は仮想であるため、オーバーライドできます。List<T>ではない。
  • あなたの質問の最後の部分はスポットです。サッカーチームは単なる選手のリストではないので、その選手のリストを含むクラスである必要があります。構成と継承を考えてください。サッカーチームに選手のリスト(名簿)がありますが、選手のリストではありません。

このコードを書いているとしたら、クラスはおそらく次のようになります。

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    // Yes. I used LINQ here. This is so I don't have to worry about
    // _roster.Length vs _roster.Count vs anything else.
    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}
155
Nean Der Thal 2014-02-12 05:45.
class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal;
}

前のコードは次のことを意味します:サッカーをしている通りからのたくさんの人、そして彼らはたまたま名前を持っています。何かのようなもの:

とにかく、このコード(私の答えから)

public class FootballTeam
{
    // A team's name
    public string TeamName; 

    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

手段:これは、管理者、プレーヤー、管理者などがいるサッカーチームです。次のようなものです。

これがあなたの論理が写真でどのように表現されているかです…

117
Tim B 2014-02-12 00:58.

これは、構成継承の典型的な例です。

この特定のケースでは:

チームは行動が追加されたプレーヤーのリストですか

または

チームは、たまたまプレーヤーのリストを含む独自のオブジェクトですか。

リストを拡張することにより、いくつかの方法で自分自身を制限します。

  1. アクセスを制限することはできません(たとえば、名簿の変更を停止するなど)。すべてのListメソッドが必要かどうかに関係なく、すべて取得できます。

  2. 他のもののリストも必要な場合はどうなりますか。たとえば、チームにはコーチ、マネージャー、ファン、設備などがあります。それらのいくつかは、それ自体がリストである可能性があります。

  3. 継承のオプションを制限します。たとえば、汎用のTeamオブジェクトを作成し、それを継承するBaseballTeam、FootballTeamなどを作成することができます。リストから継承するには、チームからの継承を行う必要がありますが、それは、さまざまなタイプのチームすべてがその名簿の同じ実装を持つことを余儀なくされることを意味します。

構成-オブジェクト内で必要な動作を提供するオブジェクトを含みます。

継承-オブジェクトは、必要な動作をするオブジェクトのインスタンスになります。

どちらにも用途がありますが、これは構成が望ましい明確なケースです。

67
Satyan Raina 2014-02-12 06:56.

誰もが指摘しているように、選手のチームは選手のリストではありません。この間違いは、おそらくさまざまなレベルの専門知識を持つ多くの人々によって引き起こされます。多くの場合、この場合のように、問題は微妙で、場合によっては非常に重大です。これらはリスコフの置換原則に違反しているため、このような設計は 不適切です。インターネットには、この概念を説明する多くの優れた記事があります。たとえば、http://en.wikipedia.org/wiki/Liskov_substitution_principle

要約すると、クラス間の親子関係で保持される2つのルールがあります。

  • 子は、親を完全に定義するものよりも少ない特性を必要とすべきではありません。
  • 親は、子を完全に定義するものに加えて、特性を必要とすべきではありません。

言い換えれば、親は子の必要な定義であり、子は親の十分な定義です。

これが解決策を考え、そのような間違いを避けるのに役立つ上記の原則を適用する方法です。親クラスのすべての操作が構造的にも意味的にも派生クラスに対して有効であるかどうかを検証することによって、仮説をテストする必要があります。

  • サッカーチームはサッカー選手のリストですか?(リストのすべてのプロパティが同じ意味でチームに適用されますか)
    • チームは同種のエンティティのコレクションですか?はい、チームはプレイヤーの集まりです
    • プレーヤーを含める順序はチームの状態を説明していますか?チームは、明示的に変更されない限り、シーケンスが保持されることを保証しますか?いいえ、いいえ
    • プレーヤーは、チーム内での順番に基づいて含まれる/ドロップされると予想されますか?番号

ご覧のとおり、リストの最初の特性のみがチームに適用されます。したがって、チームはリストではありません。リストは、チームを管理する方法の実装の詳細になるため、プレーヤーオブジェクトを格納するためにのみ使用し、チームクラスのメソッドで操作する必要があります。

この時点で、私の意見では、チームクラスはリストを使用して実装するべきではないことに注意したいと思います。ほとんどの場合、Setデータ構造(HashSetなど)を使用して実装する必要があります。

50
Sam Leach 2014-02-13 01:41.

FootballTeamメインチームと一緒にリザーブチームがある場合はどうなりますか?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
}

それをどのようにモデル化しますか?

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

関係は明らかにされていないです

またはRetiredPlayers

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
    List<FootballPlayer> RetiredPlayers { get; set; }
}

経験則として、コレクションから継承する場合は、クラスに名前を付けSomethingCollectionます。

あなたはSomethingCollection意味的に意味がありますか?タイプがのコレクションである場合にのみこれを行ってくださいSomething

それの場合はFootballTeam正しく聞こえません。ATeamは以上ですCollectionTeam他の回答が指摘しているように、Aにはコーチやトレーナーなどがいる可能性があります。

FootballCollectionサッカーのコレクション、またはサッカー用品のコレクションのように聞こえます。TeamCollection、チームのコレクション。

FootballPlayerCollectionList<FootballPlayer>あなたが本当にそれをしたいのであれば、継承するクラスの有効な名前になるプレイヤーのコレクションのように聞こえます。

本当にList<FootballPlayer>対処するのに最適なタイプです。たぶんIList<FootballPlayer>、メソッドから返す場合。

要約すれば

自問してみてください

  1. ありますか ?または持っていますか?XY XY

  2. 私のクラス名はそれらが何であるかを意味しますか?

39
El Zorko 2014-02-12 12:27.

設計>実装

公開するメソッドとプロパティは、設計上の決定です。継承する基本クラスは、実装の詳細です。前者に一歩戻る価値があると思います。

オブジェクトは、データと動作のコレクションです。

したがって、最初の質問は次のようになります。

  • このオブジェクトは、作成しているモデルでどのようなデータを構成していますか?
  • このオブジェクトはそのモデルでどのような動作を示しますか?
  • これは将来どのように変わるでしょうか?

継承は「isa」(is a)関係を意味し、構成は「has a」(hasa)関係を意味することに注意してください。アプリケーションが進化するにつれて物事がどこに行く可能性があるかを念頭に置いて、ビュー内の状況に適したものを選択してください。

具体的なタイプで考える前に、インターフェイスで考えることを検討してください。そうすることで、脳を「設計モード」にする方が簡単だと感じる人もいます。

これは、誰もが日常のコーディングでこのレベルで意識的に行うことではありません。しかし、あなたがこの種のトピックを熟考しているなら、あなたはデザインの水域を踏みにじっています。それを意識することは解放することができます。

設計の詳細を検討する

MSDNまたはVisualStudioのList <T>およびIList <T>をご覧ください。それらが公開するメソッドとプロパティを確認してください。これらの方法はすべて、あなたの見解では、誰かがFootballTeamに対してやりたいことのように見えますか?

FootballTeam.Reverse()はあなたにとって意味がありますか?FootballTeam.ConvertAll <TOutput>()はあなたが望むもののように見えますか?

これはトリックの質問ではありません。答えは本当に「はい」かもしれません。List <Player>またはIList <Player>を実装/継承すると、それらに固執します。それがあなたのモデルにとって理想的であるなら、それをしてください。

はいと判断した場合、それは理にかなっており、オブジェクトをプレーヤーのコレクション/リスト(動作)として扱いたいので、ICollectionまたはIListを実装する必要があります。必ずそうしてください。概念的に:

class FootballTeam : ... ICollection<Player>
{
    ...
}

オブジェクトにプレーヤー(データ)のコレクション/リストを含めたい場合、したがってコレクションまたはリストをプロパティまたはメンバーにしたい場合は、必ずそうしてください。概念的に:

class FootballTeam ...
{
    public ICollection<Player> Players { get { ... } }
}

プレーヤーを数えたり、追加したり、削除したりするのではなく、プレーヤーのセットのみを列挙できるようにしたい場合があります。IEnumerable <Player>は、検討するのに完全に有効なオプションです。

これらのインターフェースはどれもモデルではまったく役に立たないと感じるかもしれません。これは可能性が低いですが(IEnumerable <T>は多くの状況で役立ちます)、それでも可能です。

これらのいずれかがすべての場合において断固としてそして決定的に間違っているとあなたに話そうとする人は誰でも誤った方向に導かれます。それをあなたに伝えようとする人は誰でも、すべての場合において断固としてそして決定的に正しいと誤解されます。

実装に進む

データと動作を決定したら、実装について決定できます。これには、継承または構成を介して依存する具象クラスが含まれます。

これは大きなステップではないかもしれません。1、2秒で頭の中ですべてを実行し、入力を開始する可能性が非常に高いため、設計と実装を混同することがよくあります。

思考実験

人工的な例:他の人が述べているように、チームは必ずしも「単なる」プレーヤーの集まりではありません。チームの試合スコアのコレクションを維持していますか?あなたのモデルでは、チームはクラブと交換可能ですか?もしそうなら、そしてあなたのチームが選手の集まりであるなら、おそらくそれはスタッフの集まりおよび/またはスコアの集まりでもあります。次に、次のようになります。

class FootballTeam : ... ICollection<Player>, 
                         ICollection<StaffMember>,
                         ICollection<Score>
{
    ....
}

設計にもかかわらず、C#のこの時点では、C#は単一の継承のみをサポートしているため、とにかくList <T>から継承してこれらすべてを実装することはできません。(C ++でこのマラーキーを試したことがあれば、これは良いことだと思うかもしれません。)継承を介して1つのコレクションを実装し、構成を介して1つのコレクションを実装すると、汚く感じる可能性があります。また、ILIst <Player> .CountやIList <StaffMember> .Countなどを明示的に実装しない限り、Countなどのプロパティはユーザーを混乱させることになり、混乱するのではなく、単に苦痛になります。これがどこに向かっているのかがわかります。この道を考えているときの腸の感覚は、この方向に向かうのは間違っていると感じるかもしれません(そして、あなたがこのようにそれを実装した場合、あなたの同僚もそうかもしれません!)

短い答え(遅すぎる)

コレクションクラスから継承しないことに関するガイドラインはC#固有ではなく、多くのプログラミング言語で見られます。それは法律ではなく、知恵を受けています。その理由の1つは、実際には、構成は、理解可能性、実装可能性、および保守性の観点から、継承よりも優先されることが多いと考えられているためです。現実世界/ドメインオブジェクトでは、特に時間が経過し、コード内のオブジェクトの正確なデータと動作が経過するにつれて、抽象に深く関わっていない限り、有用で一貫性のある「isa」関係よりも有用で一貫性のある「hasa」関係を見つけることが一般的です。変化します。これにより、コレクションクラスからの継承を常に除外する必要はありません。しかし、それは示唆に富むかもしれません。

34
Dmitry S. 2014-02-11 17:25.

まず第一に、それは使いやすさに関係しています。継承を使用する場合、Teamクラスは、純粋にオブジェクト操作用に設計された動作(メソッド)を公開します。たとえば、AsReadOnly()またはCopyTo(obj)メソッドはチームオブジェクトには意味がありません。AddRange(items)メソッドの代わりに、より説明的なAddPlayers(players)メソッドが必要になる可能性があります。

LINQを使用する場合は、ICollection<T>またはなどの汎用インターフェイスを実装IEnumerable<T>する方が理にかなっています。

すでに述べたように、作曲はそれを実現する正しい方法です。プレイヤーのリストをプライベート変数として実装するだけです。

29
Mauro Sampietro 2014-02-12 06:25.

サッカーチームサッカー選手のリストではありません。サッカーチームサッカー選手のリストで構成されています!

これは論理的に間違っています:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

そしてこれは正しいです:

class FootballTeam 
{ 
    public List<FootballPlayer> players
    public string TeamName; 
    public int RunningTotal 
}
29
user1852503 2014-02-16 21:56.

あなたの質問を書き直させてください。そのため、別の視点から主題を見ることができます。

サッカーチームを代表する必要があるとき、それは基本的に名前だと理解しています。いいね:「イーグルス」

string team = new string();

その後、チームにも選手がいることに気づきました。

文字列型を拡張して、プレーヤーのリストも保持できないのはなぜですか?

問題へのあなたの入り口は恣意的です。チーム何であるかでなく、チーム何を持っているか(プロパティ)を考えてみてください。

その後、他のクラスとプロパティを共有しているかどうかを確認できます。そして、継承について考えてください。

28
stefan.schwetschke 2014-02-12 00:10.

それは文脈に依存します

チームを選手のリストと見なすと、サッカーチームの「アイデア」を1つの側面に投影します。つまり、「チーム」をフィールドにいる人々に還元します。この予測は、特定の状況でのみ正しいものです。別の文脈では、これは完全に間違っている可能性があります。チームのスポンサーになりたいと想像してみてください。したがって、チームのマネージャーと話をする必要があります。このコンテキストでは、チームはそのマネージャーのリストに投影されます。そして、これら2つのリストは、通常、あまり重複していません。他のコンテキストは、現在のプレーヤーと以前のプレーヤーなどです。

不明確なセマンティクス

したがって、チームをプレーヤーのリストと見なす場合の問題は、そのセマンティクスがコンテキストに依存し、コンテキストが変更されたときに拡張できないことです。さらに、どのコンテキストを使用しているかを表現するのは困難です。

クラスは拡張可能です

メンバーが1つしかないクラス(例IList activePlayers)を使用する場合は、メンバーの名前(およびそのコメント)を使用してコンテキストを明確にすることができます。追加のコンテキストがある場合は、メンバーを追加するだけです。

クラスはもっと複雑です

場合によっては、余分なクラスを作成するのはやり過ぎかもしれません。各クラス定義はクラスローダーを介してロードする必要があり、仮想マシンによってキャッシュされます。これにより、実行時のパフォーマンスとメモリが犠牲になります。非常に具体的な状況がある場合は、サッカーチームを選手のリストと見なしてもかまいません。ただし、この場合、実際には、 IList から派生したクラスではなく、を使用する必要があります。

結論/考慮事項

非常に具体的なコンテキストがある場合は、チームをプレーヤーのリストと見なしてもかまいません。たとえば、メソッド内で次のように記述してもまったく問題ありません。

IList<Player> footballTeam = ...

F#を使用する場合は、型の省略形を作成しても問題ありません。

type FootballTeam = IList<Player>

しかし、文脈がより広いか、さらに不明確な場合は、これを行うべきではありません。これは特に、将来使用される可能性のあるコンテキストが明確でない新しいクラスを作成する場合に当てはまります。警告サインは、クラスに属性(チームの名前、コーチなど)を追加し始めたときです。これは、クラスが使用されるコンテキストが固定されておらず、将来変更されることを明確に示しています。この場合、チームをプレーヤーのリストと見なすことはできませんが、チームの属性として(現在アクティブな、負傷していないなど)プレーヤーのリストをモデル化する必要があります。

20
Mark Brackett 2015-04-07 11:35.

他の答えは、フットボールチームが「is-a」であるList<FootballPlayer>か「has-a」List<FootballPlayer>であるかという正接でうまくいくと思うからといって、実際にはこの質問に書かれているようには答えません。

OPは主に、以下から継承するためのガイドラインの明確化を求めていますList<T>

ガイドラインには、から継承すべきではないと書かれていますList<T>。何故なの?

List<T>仮想メソッドがないためです。通常、比較的簡単に実装を切り替えることができるため、これは独自のコードではそれほど問題にはなりませんが、パブリックAPIでははるかに大きな問題になる可能性があります。

パブリックAPIとは何ですか?なぜ気にする必要がありますか?

パブリックAPIは、サードパーティのプログラマーに公開するインターフェースです。フレームワークコードを考えてください。また、参照されているガイドラインは「.NET Framework設計ガイドライン」であり、「。NETアプリケーション設計ガイドライン」ではないことを思い出してください。違いがあり、一般的に言えば、パブリックAPIの設計ははるかに厳密です。

現在のプロジェクトにこのパブリックAPIがない、またはない可能性がある場合、このガイドラインを安全に無視できますか?Listから継承し、パブリックAPIが必要であることが判明した場合、どのような問題が発生しますか?

ええ、ほとんど。とにかく自分の状況に当てはまるかどうかを確認するために、その背後にある理論的根拠を検討することをお勧めしますが、パブリックAPIを構築していない場合は、バージョン管理などのAPIの懸念について特に心配する必要はありません(これはサブセット)。

将来的にパブリックAPIを追加する場合は、実装からAPIを抽象化するか(List<T>直接公開しないことで)、ガイドラインに違反する必要があります。

なぜそれが重要なのですか?リストはリストです。何が変わる可能性がありますか?何を変更したいですか?

コンテキストによって異なりますがFootballTeam、例として使用しているFootballPlayerため、チームがサラリーキャップを超えてしまう場合は、を追加できないと想像してください。それを追加する可能な方法は次のようになります。

 class FootballTeam : List<FootballPlayer> {
     override void Add(FootballPlayer player) {
        if (this.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
        }
     }
 }

ああ...しかし、そうではないoverride Addのでvirtual(パフォーマンス上の理由で)できません。

アプリケーションを使用している場合(基本的には、あなたとすべての呼び出し元が一緒にコンパイルされることを意味します)、IList<T>コンパイルエラーの使用に変更して修正できます。

 class FootballTeam : IList<FootballPlayer> {
     private List<FootballPlayer> Players { get; set; }

     override void Add(FootballPlayer player) {
        if (this.Players.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
        }
     }
     /* boiler plate for rest of IList */
 }

ただし、サードパーティに公開している場合は、コンパイルエラーやランタイムエラーの原因となる重大な変更を加えただけです。

TL; DR-ガイドラインはパブリックAPI用です。プライベートAPIの場合は、必要なことを実行します。

18
Cruncher 2014-02-12 11:42.

人々が言うことを許可しますか

myTeam.subList(3, 5);

まったく意味がありますか?そうでなければ、それはリストであるべきではありません。

16
xpmatteo 2014-02-12 06:51.

それはあなたの「チーム」オブジェクトの振る舞いに依存します。コレクションのように動作する場合は、最初にプレーンリストで表現しても問題ない場合があります。次に、リストで反復するコードを複製し続けていることに気付くかもしれません。この時点で、プレーヤーのリストをラップするFootballTeamオブジェクトを作成するオプションがあります。FootballTeamクラスは、プレーヤーのリストで繰り返されるすべてのコードのホームになります。

それは私のコードを不必要に冗長にします。my_team.Countだけでなく、my_team.Players.Countを呼び出す必要があります。ありがたいことに、C#を使用すると、インデクサーを定義してインデックスを透過的にし、内部リストのすべてのメソッドを転送できます...しかし、これは多くのコードです。そのすべての作業で何が得られますか?

カプセル化。クライアントは、FootballTeam内で何が起こっているのかを知る必要はありません。あなたのすべてのクライアントが知っているように、それはデータベースでプレーヤーのリストを調べることによって実装されるかもしれません。彼らは知る必要はありません、そしてこれはあなたのデザインを改善します。

単純なことでは意味がありません。サッカーチームは選手のリストを「持っていません」。選手リストです。「JohnMcFootballerがSomeTeamのプレーヤーに加わった」とは言わないでください。あなたは「ジョンがSomeTeamに参加しました」と言います。「文字列の文字」に文字を追加するのではなく、文字列に文字を追加します。図書館の本に本を追加するのではなく、図書館に本を追加します。

正確に:)あなたはfootballTeam.List.Add(john)ではなくfootballTeam.Add(john)と言うでしょう。内部リストは表示されません。

15
Disillusioned 2014-02-16 10:05.

ここには多くの優れた答えがありますが、私が言及されていないものに触れたいと思います。オブジェクト指向設計とは、オブジェクトに権限を与えることです

すべてのルール、追加の作業、および内部の詳細を適切なオブジェクト内にカプセル化する必要があります。このようにして、これと相互作用する他のオブジェクトは、すべてを心配する必要はありません。実際、さらに一歩進んで、他のオブジェクトがこれらの内部をバイパスするのを積極的に防ぎたいと考えています。

から継承するとList、他のすべてのオブジェクトはあなたをリストとして見ることができます。プレーヤーを追加および削除するためのメソッドに直接アクセスできます。そして、あなたはあなたのコントロールを失うでしょう。例えば:

プレーヤーが引退したのか、辞任したのか、解雇されたのかを知ることで、プレーヤーがいつ去るかを区別したいとします。RemovePlayer適切な入力列挙型を受け取るメソッドを実装できます。しかし、継承によってList、あなたはへの直接アクセスを防止することができないであろうRemoveRemoveAllでも、とClear。その結果、あなたは実際にあなたのクラスの力を失いましたFootballTeam


カプセル化に関する追加の考え...あなたは次の懸念を提起しました:

それは私のコードを不必要に冗長にします。my_team.Countだけでなく、my_team.Players.Countを呼び出す必要があります。

あなたは正しいです、それはすべてのクライアントがあなたのチームを使用するために不必要に冗長になるでしょう。しかし、その問題は、あなたがList Playersすべてにさらされて雑多であるという事実と比較して非常に小さいので、彼らはあなたの同意なしにあなたのチームをいじることができます。

あなたは続けて言います:

単純なことでは意味がありません。サッカーチームは選手のリストを「持っていません」。選手リストです。「JohnMcFootballerがSomeTeamのプレーヤーに加わった」とは言わないでください。あなたは「ジョンがSomeTeamに参加しました」と言います。

最初のビットについては間違っています。「リスト」という単語を削除してください。チームにプレーヤーがいることは実際には明らかです。
しかし、あなたは秒で頭に釘を打ちます。クライアントがを呼び出さないようにしますateam.Players.Add(...)。あなたは彼らに電話をかけてもらいたいのですateam.AddPlayer(...)。そして、あなたの実装は(おそらくとりわけ)Players.Add(...)内部的に呼び出されます。


うまくいけば、オブジェクトに権限を与えるという目的にとって、カプセル化がいかに重要であるかがわかるでしょう。他のオブジェクトからの干渉を恐れることなく、各クラスがその仕事をうまく実行できるようにする必要があります。

12
ArturoTena 2014-02-12 08:03.

データ構造を表す正しいC#の方法は何ですか...

「すべてのモデルが間違っていますが、いくつかは便利です。」-ジョージEPボックス

「正しい方法」はなく、有用な方法だけです。

あなたやあなたのユーザーに役立つものを選択してください。それでおしまい。経済的に開発し、過度に設計しないでください。記述するコードが少なければ少ないほど、デバッグする必要のあるコードも少なくなります。(以下の版をお読みください)。

-編集済み

私の最善の答えは...それは異なります。リストから継承すると、このクラスのクライアントが公開されるべきではないメソッドに公開されます。これは主に、FootballTeamがビジネスエンティティのように見えるためです。

-エディション2

「オーバーエンジニアリングしないでください」というコメントで何を言っていたのか、心から覚えていません。私は考えているがKISSの考え方が良いガイドですが、私はリストからビジネスクラスを継承することは、それが解決さよりも多くの問題、原因を作成することを強調したい抽象漏れを

一方で、リストから継承するだけで便利な場合は限られていると思います。前の版で書いたように、それは異なります。それぞれの場合の答えは、知識、経験、個人的な好みの両方に大きく影響されます。

答えについてもっと正確に考えるのを手伝ってくれた@kaiに感謝します。

11
Paul J Abernathy 2014-02-12 04:56.

これは、「Isa」と「hasa」のトレードオフを思い出させます。スーパークラスから直接継承する方が簡単で理にかなっている場合があります。また、スタンドアロンクラスを作成し、継承元のクラスをメンバー変数として含める方が理にかなっている場合もあります。クラスの機能には引き続きアクセスできますが、インターフェイスや、クラスからの継承に起因する可能性のあるその他の制約に拘束されることはありません。

あなたはどちらをしますか?多くのものと同様に...それは文脈に依存します。私が使用するガイドは、別のクラスから継承するには、本当に「isa」の関係が必要であるということです。したがって、BMWというクラスを作成する場合、BMWは本当に車であるため、Carから継承することができます。馬は実際には哺乳類であり、哺乳類の機能はすべて馬に関連している必要があるため、馬のクラスは哺乳類のクラスから継承できます。しかし、チームはリストであると言えますか?私の知る限り、チームは実際には「リスト」ではないようです。したがって、この場合、メンバー変数としてリストがあります。

8
Nicolas Dorier 2014-02-13 03:57.

私の汚い秘密:私は人々が何を言うかは気にしません、そして私はそれをします。.NET Frameworkは、「XxxxCollection」(私の頭の例の一番上にあるUIElementCollection)で拡散されています。

だから私が言うのを止めるもの:

team.Players.ByName("Nicolas")

私がそれより良いと思うとき

team.ByName("Nicolas")

さらに、私のPlayerCollectionは、コードの重複なしに「Club」などの他のクラスで使用される可能性があります。

club.Players.ByName("Nicolas")

昨日のベストプラクティスは、明日のベストプラクティスではない可能性があります。ほとんどのベストプラクティスの背後に理由はありません。ほとんどはコミュニティ間の幅広い合意にすぎません。あなたが自分自身に尋ねるときにそれがあなたを責めるかどうかコミュニティに尋ねる代わりに、より読みやすく、維持しやすいものは何ですか?

team.Players.ByName("Nicolas") 

または

team.ByName("Nicolas")

本当に。何か疑問がありますか?ここで、実際のユースケースでList <T>を使用できないようにする他の技術的な制約を試す必要があるかもしれません。ただし、存在してはならない制約を追加しないでください。マイクロソフトがその理由を文書化していない場合、それは確かにどこからともなく来る「ベストプラクティス」です。

8
QuentinUK 2014-02-18 16:53.

ガイドラインによると、パブリックAPIは、リスト、セット、辞書、ツリーなどを使用しているかどうかの内部設計上の決定を明らかにしてはなりません。「チーム」は必ずしもリストではありません。リストとして実装することもできますが、パブリックAPIのユーザーは、知る必要がある場合にクラスを使用する必要があります。これにより、パブリックインターフェイスに影響を与えることなく、決定を変更し、別のデータ構造を使用できます。

7
Shital Shah 2014-02-16 21:50.

彼らList<T>が「最適化されている」と言うとき、私は彼らがそれが少し高価な仮想メソッドのような機能を持っていないことを意味したいと思います。したがって、問題はList<T>パブリックAPI公開すると、ビジネスルールを適用したり、後でその機能をカスタマイズしたりする機能が失われることです。ただし、この継承されたクラスをプロジェクト内の内部として使用している場合(APIとして何千もの顧客/パートナー/他のチームに公開される可能性があるのではなく)、時間を節約でき、それが必要な機能である場合は問題ありません。重複。から継承することの利点はList<T>、予見可能な将来にカスタマイズされることのない多くのダムラッパーコードを排除できることです。また、クラスにList<T>APIの存続期間とまったく同じセマンティクスを明示的に持たせたい場合は、それでも問題ありません。

FxCopのルールがそう言っている、または誰かのブログがそれが「悪い」習慣だと言っているという理由だけで、多くの人が余分な仕事をたくさんしているのをよく見ます。多くの場合、これはコードをデザインパターンpaloozaの奇妙さに変えます。多くのガイドラインと同様に、例外設けることができるガイドラインとして扱います。

4
Null511 2018-01-20 16:43.

これらの回答のほとんどがそうであるように、私は複雑な比較をしていませんが、この状況を処理するための私の方法を共有したいと思います。を拡張IEnumerable<T>することにより、のTeamすべてのメソッドとプロパティを公開することなく、クラスがLinqクエリ拡張をサポートできるようにすることができますList<T>

class Team : IEnumerable<Player>
{
    private readonly List<Player> playerList;

    public Team()
    {
        playerList = new List<Player>();
    }

    public Enumerator GetEnumerator()
    {
        return playerList.GetEnumerator();
    }

    ...
}

class Player
{
    ...
}
3
Ivan Nikitin 2014-02-16 21:59.

クラスユーザーがすべてのメソッドとプロパティを必要とする場合**リストにある場合は、そこからクラスを派生させる必要があります。それらが必要ない場合は、リストを囲み、クラスユーザーが実際に必要とするメソッドのラッパーを作成します。

パブリックAPIや、多くの人が使用するその他のコードを作成する場合、これは厳格なルールです。小さなアプリで開発者が2人以下の場合は、このルールを無視してかまいません。これにより、時間を節約できます。

小さなアプリの場合は、厳密性の低い別の言語を選択することも検討してください。Ruby、JavaScript-より少ないコードを書くことができるものなら何でも。

3
Alexey 2015-04-28 01:35.

エッフェル塔の発明者であり、契約による設計であるバートランド・メイヤーは、まぶたを打つことなくTeam継承していたであろうことを付け加えたかっただけList<Player>です。

彼の著書「オブジェクト指向ソフトウェアの構築」では、長方形のウィンドウに子ウィンドウを含めることができるGUIシステムの実装について説明しています。彼は単純にしているWindowの両方を継承Rectangleし、Tree<Window>実装を再利用します。

ただし、C#はEiffelではありません。後者は、多重継承と機能の名前変更をサポートしています。C#では、サブクラス化すると、インターフェイスと実装の両方を継承します。実装をオーバーライドできますが、呼び出し規約はスーパークラスから直接コピーされます。エッフェル塔では、しかし、あなたは、publicメソッドの名前を変更することができますので、あなたが名前を変更することができますAddし、RemoveHireし、FireあなたにTeam。のインスタンスTeamがにアップキャストされたList<Player>場合、呼び出し元はそれを使用AddRemoveて変更しますが、仮想メソッドHireFireが呼び出されます。

3
marsh-wiggle 2019-07-21 04:55.

シリアル化の問題

1つの側面が欠けています。List <>から継承するクラスは、XmlSerializerを使用して正しくシリアル化できません。その場合、代わりにDataContractSerializerを使用する必要があります。そうでない場合は、独自のシリアル化の実装が必要です。

public class DemoList : List<Demo>
{
    // using XmlSerializer this properties won't be seralized:
    string AnyPropertyInDerivedFromList { get; set; }     
}

public class Demo
{
    // this properties will be seralized
    string AnyPropetyInDemo { get; set; }  
}

さらに読む:クラスがList <>から継承される場合、XmlSerializerは他の属性をシリアル化しません

2
Eniola 2016-07-10 07:14.

私はあなたの一般化に同意しないと思います。チームは単なるプレーヤーの集まりではありません。チームには、名前、エンブレム、管理/管理スタッフのコレクション、コーチングクルーのコレクション、プレーヤーのコレクションなど、はるかに多くの情報があります。したがって、適切には、FootballTeamクラスには3つのコレクションがあり、それ自体がコレクションではない必要があります。現実の世界を適切にモデル化する場合。

Specialized StringCollectionのように、オブジェクトが内部ストアに追加または内部ストアから削除される前の検証やチェックなど、他のいくつかの機能を提供するPlayerCollectionクラスを検討できます。

おそらく、PlayerCollectionの概念は、好みのアプローチに適していますか?

public class PlayerCollection : Collection<Player> 
{ 
}

そして、FootballTeamは次のようになります。

public class FootballTeam 
{ 
    public string Name { get; set; }
    public string Location { get; set; }

    public ManagementCollection Management { get; protected set; } = new ManagementCollection();

    public CoachingCollection CoachingCrew { get; protected set; } = new CoachingCollection();

    public PlayerCollection Players { get; protected set; } = new PlayerCollection();
}
2
cdiggins 2018-10-07 19:01.

クラスよりもインターフェースを優先する

クラスは、クラスから派生することを避け、代わりに必要な最小限のインターフェースを実装する必要があります。

継承はカプセル化を破ります

クラスから派生すると、カプセル化が壊れます

  • コレクションの実装方法に関する内部の詳細を公開します
  • 適切でない可能性のあるインターフェイス(パブリック関数とプロパティのセット)を宣言します

特に、これによりコードのリファクタリングが困難になります。

クラスは実装の詳細です

クラスは実装の詳細であり、コードの他の部分から隠す必要があります。要するに、aSystem.Listは抽象データ型の特定の実装であり、現在および将来的に適切である場合とそうでない場合があります。

概念的には、System.Listデータ型が「リスト」と呼ばれているという事実は、少し厄介です。ASystem.List<T>は、要素を追加、挿入、および削除するための償却されたO(1)操作と、要素の数を取得したり、インデックスによって要素を取得および設定したりするためのO(1)操作をサポートする変更可能な順序付きコレクションです。

インターフェイスが小さいほど、コードの柔軟性が高くなります

データ構造を設計する場合、インターフェースが単純であるほど、コードはより柔軟になります。これを実証するために、LINQがどれほど強力であるかを見てください。

インターフェースの選び方

「リスト」と考えるときは、「野球選手のコレクションを代表する必要がある」と自分に言い聞かせることから始めるべきです。それで、これをクラスでモデル化することにしたとしましょう。最初にすべきことは、このクラスが公開する必要のあるインターフェースの最小量を決定することです。

このプロセスをガイドするのに役立ついくつかの質問:

  • カウントする必要がありますか?実装を検討しない場合IEnumerable<T>
  • このコレクションは、初期化された後に変更されますか?考慮しない場合IReadonlyList<T>
  • インデックスでアイテムにアクセスできることは重要ですか?検討するICollection<T>
  • コレクションにアイテムを追加する順序は重要ですか?多分それはISet<T>
  • 本当にこれらのものが必要な場合は、先に進んで実装してくださいIList<T>

このようにして、コードの他の部分を野球選手コレクションの実装の詳細に結合することはなく、インターフェイスを尊重する限り、実装方法を自由に変更できます。

このアプローチを採用することで、コードの読み取り、リファクタリング、および再利用が容易になることがわかります。

ボイラープレートの回避に関する注意事項

最新のIDEでのインターフェースの実装は簡単なはずです。右クリックして「インターフェースの実装」を選択します。次に、必要に応じて、すべての実装をメンバークラスに転送します。

とは言うものの、ボイラープレートをたくさん書いていることに気付いた場合、それは潜在的に、本来あるべきよりも多くの機能を公開しているためです。これは、クラスから継承してはならないのと同じ理由です。

また、アプリケーションにとって意味のある小さなインターフェースを設計することもできます。また、それらのインターフェースを必要な他のインターフェースにマップするためのヘルパー拡張関数をいくつか設計することもできます。これは、LinqArrayライブラリの独自のIArrayインターフェイスで採用したアプローチです

0
phoog 2020-05-22 09:48.

いつ受け入れられますか?

エリック・リペットを引用するには:

メカニズムを拡張するメカニズムを構築しているときList<T>

たとえば、次のAddRangeメソッドがないことにうんざりしていますIList<T>

public interface IMoreConvenientListInterface<T> : IList<T>
{
    void AddRange(IEnumerable<T> collection);
}

public class MoreConvenientList<T> : List<T>, IMoreConvenientListInterface<T> { }

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

Total War:Warhammer:Kotakuレビュー

Total War:Warhammer:Kotakuレビュー

私はこのゲームを嫌う準備ができていました。先週の前に、Total War:Warhammerについての私の考えがありました:それでもここに私は、私の手にある完成品であり、私は変わった男です。

涙の道:軍事化された帝国主義勢力がスタンディングロックキャンプを占領

涙の道:軍事化された帝国主義勢力がスタンディングロックキャンプを占領

スタンディングロックスー族のメンバーと水の保護者は、ノースダコタ州のスタンディングロックにあるオセティサコウィンキャンプを去ります。(Twitter経由のCNNスクリーンショット)火と煙がスカイラインを覆い、スタンディングロックスー族のメンバーと水の保護者が、聖なるものを守りながら建てた家、オセティサコウィン(セブンカウンシルファイアーズ)キャンプから行進し、太鼓を打ち、歌い、祈りました。ダコタアクセスパイプラインとしても知られる「ブラックスネーク」からの土地。

シアーズとKマートはイヴァンカ・トランプの商品を自分たちで取り除いています

シアーズとKマートはイヴァンカ・トランプの商品を自分たちで取り除いています

写真:APシアーズとKマートは、イヴァンカ・トランプのトランプホームアイテムのコレクションも、誰も購入したくないために削除しました。シアーズとKマートの両方の親会社であるシアーズホールディングスは、土曜日のABCニュースへの声明で、彼らが気にかけていると辛抱強く説明しましたトランプラインを売り続けるにはお金を稼ぐことについてあまりにも多く。

ポテトチップスでたった10分でスペインのトルティーヤを作る

ポテトチップスでたった10分でスペインのトルティーヤを作る

伝統的なスペインのトルティーヤは通常、オリーブオイルで柔らかくなるまで調理されたポテトから始まります(30分以上かかる場合があります)が、ケトルで調理されたポテトチップスの助けを借りてわずか10分でテーブルに置くことができます。上のビデオはすべてがバラバラにならないように裏返す方法を含め、レシピ全体を説明しますが、必要なのは4〜5個の卵と3カップのケトルチップスだけです。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language