Фильтр запросов Linq содержит со списком ‹T› несколько элементов

Я хочу запросить список с помощью Linq, но отфильтровать по другому списку, содержащему два элемента (имя, статус) в моем примере. Это навеяно старым вопросом, который я адаптировал к своей проблеме. LINQ: содержит и лямбда-запрос (в этом ответе он работает только для одного элемент т.е. статус)

Я пытаюсь использовать метод "содержит", но не смог отфильтровать свой список.

Я должен получить результат только с двумя зданиями (два, пять). Кто-нибудь знает, где меня остановили?

Спасибо

Цитата

    public class Building
    {
        public enum StatusType
        {
            open,
            closed,
            weird,
        };

        public string Name { get; set; }
        public StatusType Status { get; set; }
    }

    private static readonly List<Building> BuildingList = new List<Building>()
    {
        new Building() {Name = "one", Status = Building.StatusType.open},
        new Building() {Name = "two", Status = Building.StatusType.closed},
        new Building() {Name = "three", Status = Building.StatusType.weird},
        new Building() {Name = "four", Status = Building.StatusType.open},
        new Building() {Name = "five", Status = Building.StatusType.closed},
        new Building() {Name = "six", Status = Building.StatusType.weird},
    };

    private  void GetResult()
    {
        var buildingSelect = new List<Building>
        {
            new Building() {Name = "two", Status = Building.StatusType.closed},
            new Building() {Name = "five", Status = Building.StatusType.closed}
        };

        var q = (from building in BuildingList
            where buildingSelect.Contains(building.Name, building.Status)
            select building).ToList();

        dataGridView1.DataSource = q;
    }

person GYCO50    schedule 17.06.2016    source источник


Ответы (3)


Основная проблема вашего LINQ заключается в том, что вы пытаетесь сравнить равенство двух Building, которые LINQ может сравнивать только по их ссылкам, потому что Building не реализует IEquatable<Building> и не переопределяет object.Equals.

Один из способов решить эту проблему - вручную указать, какие свойства сравнивать на равенство в соответствии с ответом @ Wayne.

Другой способ: если Building экземпляры должны быть приравнены по их значениям, а не по их ссылкам, реализовать IEquatable<Building> и переопределить object.Equals:

public class Building : IEquatable<Building>
{
    public Building(string name, StatusType status)
    {
        Name = name;
        Status = status;
    }

    public enum StatusType
    {
        open,
        closed,
        weird,
    };

    public string Name { get; }

    public StatusType Status { get; }

    public static bool operator ==(Building left, Building right)
        => Equals(left, right);

    public static bool operator !=(Building left, Building right)
        => !Equals(left, right);

    public override bool Equals(object obj) => Equals(obj as Building);

    public bool Equals(Building other)
    {
        if (ReferenceEquals(this, other))
        {
            return true;
        }

        if (ReferenceEquals(other, null) || GetType() != other.GetType())
        {
            return false;
        }

        return Name == other.Name && Status == other.Status;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + Name?.GetHashCode() ?? 0;
            hash = hash * 23 + Status.GetHashCode();
            return hash;
        }
    }
}

Таким образом, ваш исходный код будет работать, потому что List.Contains теперь будет использовать вашу реализацию IEquatable<Building> для проверки равенства.

person rexcfnghk    schedule 18.06.2016
comment
Спасибо, rexcfnghk, благодаря этому я изучил и экспериментировал с реализацией IEquatable. Теперь Contains работает с небольшими обновлениями в моем исходном коде ›'where buildingSelect.Contains (new Building (building.Name, building.Status))'. Удалить в списке тоже работает. Здорово. Большое спасибо. - person GYCO50; 20.06.2016
comment
Если мой ответ вам помог, примите его как ответ - person rexcfnghk; 20.06.2016

Вы имеете в виду что-то вроде этого?

    var q = from b in BuildingList
        from bs in buildingSelect
        where b.Name == bs.Name && b.Status == bs.Status
        select b;

или возможно:

    var q = from b in BuildingList
        join bs in buildingSelect
        on new { b.Name, b.Status } equals new { bs.Name, bs.Status }
        select b;
person lesscode    schedule 18.06.2016
comment
У меня осталась проблема, так как мне нужно изменить список buildingSelect, удалив несколько строк вроде этой: 'code buildingSelect.Remove (new Building () {Name = two, Status = Building.StatusType.closed}); «Это не работает. Есть идеи исправить это. С Уважением. - person GYCO50; 18.06.2016
comment
См. Ответ rexcfnghk. List.Remove будет использовать ссылочное равенство, если ваш Building объект не реализует GetHashCode и Equals, поэтому ваш new Building никогда не будет существовать в коллекции buildingSelect и по определению не может быть удален. - person lesscode; 18.06.2016

Вы можете либо переопределить равенство в самом классе, ЕСЛИ это имеет смысл.

Или просто выполните обычную проверку с помощью Any(), например:

    var q = (from building in BuildingList
        where buildingSelect.Any(b => b.Name == building.Name 
                                      && b.Status == building.Status)
        select building).ToList();
person Meligy    schedule 18.06.2016