Как преобразовать результаты linq в HashSet или HashedSet

У меня есть свойство в классе, который является ISet. Я пытаюсь получить результаты запроса linq в это свойство, но не могу понять, как это сделать.

В основном, ища последнюю часть этого:

ISet<T> foo = new HashedSet<T>();
foo = (from x in bar.Items select x).SOMETHING;

Также можно сделать следующее:

HashSet<T> foo = new HashSet<T>();
foo = (from x in bar.Items select x).SOMETHING;

Изменить: Это то, что я закончил:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
    return new HashSet<T>(source);
}

public static HashedSet<T> ToHashedSet<T>(this IEnumerable<T> source)
{
    return new HashedSet<T>(source.ToHashSet());
}

Ответ 1

Я не думаю, что там что-то построено, в котором это делает... но очень легко написать метод расширения:

public static class Extensions
{
    public static HashSet<T> ToHashSet<T>(
        this IEnumerable<T> source,
        IEqualityComparer<T> comparer = null)
    {
        return new HashSet<T>(source, comparer);
    }
}

Обратите внимание, что здесь действительно нужен метод расширения (или, по крайней мере, общий метод какой-либо формы), потому что вы не можете явно выразить тип T:

var query = from i in Enumerable.Range(0, 10)
            select new { i, j = i + 1 };
var resultSet = query.ToHashSet();

Вы не можете сделать это с явным вызовом конструктора HashSet<T>. Мы полагаемся на вывод типа для общих методов, чтобы сделать это для нас.

Теперь вы можете назвать его ToSet и вернуть ISet<T> - но я бы придерживался ToHashSet и конкретного типа. Это согласуется со стандартными операторами LINQ (ToDictionary, ToList) и допускает дальнейшее расширение (например, ToSortedSet). Вы также можете указать перегруз с указанием используемого сравнения.

Ответ 2

Просто передайте IEnumerable в конструктор для HashSet.

HashSet<T> foo = new HashSet<T>(from x in bar.Items select x);

Ответ 3

Как указано в @Joel, вы можете просто перечислить свой список. Если вы хотите использовать метод расширения, вы можете сделать:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
    return new HashSet<T>(items);
}

Ответ 4

Если вам нужен только доступ только для чтения к набору, а источник - это параметр для вашего метода, я бы пошел с

public static ISet<T> EnsureSet<T>(this IEnumerable<T> source)
{
    ISet<T> result = source as ISet<T>;
    if (result != null)
        return result;
    return new HashSet<T>(source);
}

Причина в том, что пользователи могут вызывать ваш метод с помощью ISet уже, поэтому вам не нужно создавать копию.

Ответ 5

Это довольно просто:)

var foo = new HashSet<T>(from x in bar.Items select x);

и да T - тип, заданный OP:)

Ответ 6

Вы можете просто использовать конструктор IEnumerable HashSet.

HashSet<T> foo = new HashSet<T>((from x in bar.Items select x).ToArray());

Ответ 7

Джон отвечает, прекрасно. Единственное предостережение в том, что, используя NHibernate HashedSet, мне нужно преобразовать результаты в коллекцию. Есть ли оптимальный способ сделать это?

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToArray()); 

или

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToList()); 

Или я пропущу что-то еще?

Ответ 8

Вместо простого преобразования IEnumerable в HashSet часто бывает удобно преобразовать свойство другого объекта в HashSet. Вы можете написать это как:

var set = myObject.Select(o => o.Name).ToHashSet();

но я предпочитаю использовать селекторы:

var set = myObject.ToHashSet(o => o.Name);

Они делают то же самое, а второе явно короче, но я считаю, что идиома лучше подходит моему мозгу (я думаю, что это похоже на ToDictionary).

Здесь используется метод расширения с поддержкой пользовательских сопоставлений в качестве бонуса.

public static HashSet<TKey> ToHashSet<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> selector,
    IEqualityComparer<TKey> comparer = null)
{
    return new HashSet<TKey>(source.Select(selector), comparer);
}