質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

ToDictionaryメソッドの引数が1つの構文に関して(拡張メソッド、LINQ to Object)。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 674

score 352

前提・実現したいこと

こちらのサイトにToDictionaryメソッドの解説がされていますが、
構文と実際に処理しているコードの形式が合わなかったり、説明で分からない点があります。
ご教示お願いします。

試したこと

下記2つのコードが同等であることは、感覚的になんとなく理解できました。

Dictionary<int, Skill> skillDictonary = new Dictionary<int, Skill> ();
foreach (Skill skill in skillList) {
    skillDictionary.Add (skill.Id, skill);
}
Dictionary<int, Skill> skillDictonary = skillList.ToDictionary (skill => skill.Id);

この解説がよくわからないです。

ここで使ったオーバーロードは、引数にデリゲートをとります。
IEnumerable<T>の各要素がDictionaryのバリューになります。
引数に渡したデリゲートを、各要素(バリュー)に適用した結果が要素(バリュー)に対応するキーとなります。


引数の「skill => skill.Id」というのはデリゲートなんですか?
また、「引数に渡したデリゲートを、各要素(バリュー)に適用した結果」というのも、どのように適用したのかわからないです。

デリゲートは登録しておいたメソッドを呼び出すものだと認識していましたが、
登録しておいたメソッドはないと思ったからです。

また、下記の構文も
「skillList.ToDictionary (skill => skill.Id);」の形式と全然違うように思えます。

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
)


例えば、構文だと引数が「TSource, TKey」のようにカンマ区切りで2個与えられていますが、
コードでは、skill => skill.Idの1つの引数を与えていますよね。
また、デリゲートの件と重複しますが、skill => skill.Id がどういう適用になるのかわからないです。
この形式がラムダ式で、「(引数)=>引数を使った処理」という形式なのはわかりますが。

ご教示お願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

この「ぐるぐる回す」というのは、どの記述でわかるものですか?

すみません、半分寝ぼけてたのもあってちょっと誤解を与える記述の仕方でした。

static なメソッドで、第一引数がthis データ型 引数名となっているものは拡張メソッドです。
拡張メソッドの場合、呼び出し元は第一引数自体を引数として渡さずに呼び出します。
この場合のToDictionaryはIEnumerableの拡張メソッドである「LINQ to Object」として提供されています。
こういったLINQ to Objectはいわゆるコレクション変換なので、内部でぐるぐる回しているということになります。

というわけで、「拡張メソッドなので」というより、「LINQ to Objectなので」と書くべきでした。
見たわけではないですが、今回の場合のToDictionaryはこんな感じに内部はなっているはずです。

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector)
{
  Dictionary<TKey, TSource> result = new Dictionary<TKey, TSource>();
  foreach (TSource value in source)
  {
    result.Add(new KeyValuePair( keySelector(value), value));
  }
  return result;
}

(skill) =>
{
return skill.Id;
}
こちらが、Func<TSource, TKey> keySelectorに該当するということですか?

はい、そうです。
ちなみにラムダ式をFuncデリゲートの一時変数に保管してから渡すとこんな感じになります。

Func<Skill, int> temp_func = (skill) => skill.Id;
var skillDictionary = skillList.ToDictionary(temp_func);

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/15 00:43

    ご回答ありがとうございます。
    >static なメソッドで、第一引数がthis データ型 引数名となっているものは拡張メソッドです。
    なるほど、拡張メソッドとはそういうものなのですね。
    検索して調べました。
    メモの為に貼っておきます。
    https://webbibouroku.com/Blog/Article/extension-cs

    また、ToDictionaryは「LINQ to Object」だから、
    内部でぐるぐる回しているということなのですね。
    理解できました。
    ありがとうございました。

    キャンセル

0

引数の「skill => skill.Id」というのはデリゲートなんですか?

デリゲートの部分は少し自分でも曖昧なところはあるので恐縮ですがMSDNから

ラムダ式は、 デリゲート型 または 式ツリー型 を作成するために使用できる 匿名関数 です。

とあります。
なので、ラムダ式をデリゲート型に渡していることになります。

次に、引数についてですが

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
)


の Func<TSource, TKey> は TSource型の引数を一つ受け取り、TKey型の値を返す デリゲートになります。
で、 skillList.ToDictionary (skill => skill.Id); で渡しているラムダ式の部分はいろいろ省略されている記述なのですが、省略なしで書くと

skillList.ToDictionary( (skill) =>
  {
    return skill.Id;
  });


といった感じになり、これがkeySelectorとして扱われています。

次いでに ToDictionary は拡張メソッドなので、skillListの各要素のIEnumerable<TSource>をぐるぐる回して実行している形になります。

なので、この一行のメソッドをそのまま展開すると、こんな感じになるのだと思います。

foreach (Skill skill in skillList)
{
  skillDictonary.Add( new KeyValuePair(
     (skill) => { return skill.Id; },
     skill));
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/14 13:36

    ご回答ありがとうございます。
    だいぶ全体像が見えました。ありがとうございます。

    > ToDictionary は拡張メソッドなので、skillListの各要素のIEnumerable<TSource>をぐるぐる回して
    この「ぐるぐる回す」というのは、どの記述でわかるものですか?
    もしくは可能ならばドキュメントでそれが明記されているものを教えていただけますか?

    それとも、
    this IEnumerable<TSource> source,
    この記述が要素をぐるぐる回すという意味になっていますか?

    また、念のため確認ですが、
    (skill) =>
    {
    return skill.Id;
    }
    こちらが、Func<TSource, TKey> keySelectorに該当するということですか?

    キャンセル

  • 2019/01/14 15:30

    > この「ぐるぐる回す」というのは、どの記述でわかるものですか?

    すみません、半分寝ぼけてたのもあってちょっと誤解を与える記述の仕方でした。

    static なメソッドで、第一引数が``this データ型 引数名``となっているものは拡張メソッドです。
    拡張メソッドの場合、呼び出し元は第一引数自体を引数として渡さずに呼び出します。
    この場合の``ToDictionary``はIEnumerableの拡張メソッドである「LINQ to Object」として提供されています。
    こういった``LINQ to Object``はいわゆるコレクション変換なので、内部でぐるぐる回しているということになります。

    というわけで、「拡張メソッドなので」というより、「LINQ to Objectなので」と書くべきでした。
    見たわけではないですが、今回の場合の``ToDictionary``はこんな感じに内部はなっているはずです。

    ``` C#
    public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector)
    {
    Dictionary<TKey, TSource> result = new Dictionary<TKey, TSource>();
    foreach (TSource value in source)
    {
    result.Add(new KeyValuePair( keySelector(value), value));
    }
    return result;
    }
    ```

    > (skill) =>
    > {
    > return skill.Id;
    > }
    > こちらが、Func<TSource, TKey> keySelectorに該当するということですか?

    はい、そうです。
    ちなみにラムダ式をFuncデリゲートの一時変数に保管してから渡すとこんな感じになります。
    ``` C#
    Func<Skill, int> temp_func = (skill) => skill.Id;
    var skillDictionary = skillList.ToDictionary(temp_func);
    ```

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る