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

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

ただいまの
回答率

90.51%

  • C#

    9042questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • LINQ

    134questions

    LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

Enumerable.Distinct<T> 拡張メソッドでカスタムクラスを除外できない理由がわからない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,346

twyujiro15

score 198

疑問

カスタムクラスのシーケンスに対する Enumerable.Distinct<T> 拡張メソッドの挙動について、
既定の等値比較子による比較で "重複" とみなされない理由がわかりません。

調査内容

下記公式サイトからの引用です。
MSDN:Enumerable.Distinct<TSource> メソッド (IEnumerable<TSource>)

既定の等値比較演算子 Default は、IEqualityComparer<T> ジェネリック インターフェイスを実装している型の値を比較するために使用されます。 カスタム データ型を比較するには、このインターフェイスを実装し、その型の GetHashCode メソッドと Equals メソッドを独自に用意する必要があります。 

つまり IEqualityComparer<T> を実装しろ、ということです。
こう書いてあるのだからそうすべきだ、といってしまえばそれまでなのですが、
Distinct<T> の内部実装を覗くと次のようなコードが見つかります。

if (this.slots[i].hashCode == num && this.comparer.Equals(this.slots[i].value, value))
{
    return true;
}

つまりハッシュコードが一致していることと、
既定の Comparer による等値比較が一致していることで
"重複" とみなしているということです。

試してみたコード

というわけで以下のようなコードを試してみました。
単純な人物情報を表す Person クラスを作成し、
インスタンスが重複する要素を持つリストに対して Distinct<T> を使用しています。

途中、ハッシュコードによる比較や既定の等値比較演算子による比較をおこなっていますが、
どれも "一致" という結果となります。
これにも関わらず Distinct<T> 拡張メソッドによって "重複" とみなされない理由はなんでしょうか。

namespace LinqTest
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Person() { Name = "田中 淳平", Date = new DateTime(2011, 5, 2) };
            var p2 = new Person() { Name = "鈴木 ほのか", Date = new DateTime(2014, 3, 24) };
            var p3 = new Person() { Name = "小池 哲司", Date = new DateTime(2002, 6, 13) };
            var people = new List<Person>() { p1, p2, p1, p3 };
            var comparer = EqualityComparer<Person>.Default;

            if (people[0].GetHashCode() == people[2].GetHashCode()) Console.WriteLine("GetHashCode は一致。");
            if (comparer.Equals(people[0], people[2])) Console.WriteLine("既定の Comparer による等値比較は一致。");
            if (comparer.GetHashCode(people[0]) == comparer.GetHashCode(people[2])) Console.WriteLine("既定の Comparer による GetHashCode は一致。");

            foreach (var p in people)
            {
                Console.WriteLine(p.Name + " (" + p.GetHashCode() + ")");
            }

            Console.WriteLine("重複要素を除外します。");
            var newPeople = people.Distinct();
            foreach (var p in people)
            {
                Console.WriteLine(p.Name + " (" + p.GetHashCode() + ")");
            }

            Console.ReadKey();
        }
    }
}
namespace LinqTest
{
    using System;

    /// <summary>
    /// 人物データを表します。
    /// </summary>
    public class Person
    {
        /// <summary>
        /// 氏名を取得または設定します。
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 更新日付を取得または設定します。
        /// </summary>
        public DateTime Date { get; set; }
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

こんにちは。
質問のコードでちゃんと除外されますよ?

問題はここにあります。

var newPeople = people.Distinct();
foreach (var p in people) // !
{
    Console.WriteLine(p.Name + " (" + p.GetHashCode() + ")");
}


peopleをDistinctしたものをnewPeopleに格納しているのに、Distinctする前のpeopleを列挙しているからです。

Distinctの挙動は、質問者さんの理解で完璧に正しいです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/12/08 09:22

    あわわわわ…。
    穴があったら入りたいです!!
    なんとアホなことを!恥ずかしい!

    ありがとうございました。

    キャンセル

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

  • C#

    9042questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • LINQ

    134questions

    LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。