Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Distinct в Liste


Автор: Stratus 9.8.2011, 23:36
Коллеги не могу разобраться как использовать по назначению Distinct, когда изначально есть List объективов и 
distinct нужно применить к определенному полю объекта?

Вот этот код не работает...
Код

    class Program
    {
        static List<Objects> Obj = new List<Objects>(); 
        static string[] DistinctStrings;

        static void Main(string[] args)
        {
            Obj.Add(new Objects("TextF1","Text_F21"));
            Obj.Add(new Objects("TextF1_1","Text_F21_1"));
            Obj.Add(new Objects("TextF1_1","Text_F21_1"));

            DistinctStrings = Obj.Distinct().ToString().ToArray();

            foreach (string DS in DistinctStrings)
                Console.WriteLine(DS);

            Console.ReadLine( );
        }
    }

    class Objects
    {
        string Field1;
        string Field2;

        public Objects(string Field1, string Field2)
        {
            this.Field1 = Field1;
            this.Field2 = Field2;
        }

        public string F1 {get {return Field1; }}
        public string F2 {get {return Field2; }}
    }

Автор: WarHog 10.8.2011, 09:21
Во-первых, в твоем случае Distinct'у нужно передать класс, реализующий IEqualityComparer<Objects>, в котором ты задаешь, по каким критериям будет работать дистинкт. Например, в простейшем случае, если тебе нужно применение к F1, то нужно что-то вроде такого:
Код

class Comparer : IEqualityComparer<Objects>
    {
        public bool Equals(Objects x, Objects y)
        {
            return x.F1 == y.F1;
        }

        public int GetHashCode(Objects obj)
        {
            return obj.F1.GetHashCode();
        }
    }

Но в таком случае для каждого нового типа сравнения придется определять новый класс компарера. Лучше сделать его универсальным, как-то так:
Код

class UniversalComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _compareFunc;
        private readonly Func<T, int> _hashFunc;

        public UniversalComparer(Func<T, T, bool> compareFunc, Func<T, int> hashFunc)
        {
            _compareFunc = compareFunc;
            _hashFunc = hashFunc;
        }

        public bool Equals(T x, T y)
        {
            return _compareFunc(x, y);
        }

        public int GetHashCode(T obj)
        {
            return _hashFunc(obj);
        }
    }

здесь в конструкторе ты передаешь делегаты, которые определяют сравнение твоих объектов между собой и взятие хэша для объекта. Работать это все будет так:
Код

            var res = Obj.Distinct(
                new UniversalComparer<Objects>((o1, o2) => o1.F1 == o2.F1, o => o.F1.GetHashCode()));

Соответственно, в делегатах вместо F1 можно взять любое свойство.

Во-вторых, такое
Код

.Distinct().ToString().ToArray()

приведет к тому, что у тебя будет массив из Char'ов, который ты потом запихиваешь в foreach и Char пытаешься неявно прокастить в строке.
Делай что-то вроде
Код

var res = Obj.Distinct(...);
            foreach (var DS in res)
                Console.WriteLine(DS.F1);

ну или переопределяй ToString у Objects'а  smile

Автор: Stratus 10.8.2011, 18:32
Отлично. Спасибо за развернутый ответ!
Я честно говоря пробовал создать Comparer реализовав этот интерфейс прям в классе обьекта Objects, но потом не смог его оттуда вытащить.
Получается из-за одного поля создавать отдельный класс...
А насколько этот способ быстрый если например сравнить его с обычным перебором т.е. выделить поле из объекта прошедшись по нему перебором, а затем взять Distinct()?

Автор: WarHog 10.8.2011, 21:59
Цитата

Получается из-за одного поля создавать отдельный класс...

Ну если ты сделаешь дженерик компаратор, то сможешь его использовать для любого типа.
Цитата

А насколько этот способ быстрый если например сравнить его с обычным перебором т.е. выделить поле из объекта прошедшись по нему перебором, а затем взять Distinct()?

т.е. сделать что-то вроде
Код

Obj.Select(o => o.F1).Distinct();

? так будет, во-первых, только медленнее, а, во-вторых, на выходе ты получаешь IEnumerable<string>, в отличие от IEnumerable<T>
Медленнее, потому что будет лишний select и потому что Distinct, не получив компарера, будет использовать в данном случае дефолтный  GenericEqualityComparer<string> и дергать внутри тот же самый x.Equals(y).

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)