#c# #c#-9.0
Вопрос:
public static class TestRecordTypes
{
public static void RunTest()
{
List<Person> people = new List<Person>()
{
new Person("John", "Doe", "home"),
new Person("John", "Doe", "Away"),
new Person("Billy", "Doe", "home"),
new Person("Billy", "Doe", "home"),
new Person("Batty", "Bo", "home"),
};
var peopleHash = people.ToHashSet();
Debug.WriteLine($"{peopleHash}");
}
}
public record Person(string FirstName, string LastName)
{
string _location;
public Person(string firstName, string lastName, string location):
this(firstName, lastName)
{
_location = location;
}
}
С помощью этого теста я надеялся, что peopleHash
в списке будет только 3 записи, но я получаю 4, так как равенство, по-видимому, включает параметр non init location
.
Есть ли способ получить список из 3 записей, которые я хочу, где сравнение выполняется только для свойств инициализации и игнорирует дополнительный параметр местоположения?
Комментарии:
1. Вы всегда можете реализовать
IEqualityComparer<Person>
и передать этоToHashSet
2. Я думаю, что мог бы, но если я делаю это, я могу также использовать стандартный класс. Я чувствую, что должен быть декоратор, который я могу использовать, чтобы заставить компилятор игнорировать поле местоположения.
3. @John Вы можете написать генератор исходного кода и пользовательский атрибут для этого.
Ответ №1:
Вы хотите изменить IEquatable<Person>
реализацию. Для этого вы можете добавлять Equal
и переопределять GetHashCode
в record
:
public record Person(string FirstName, string LastName)
{
string _location;
public Person(string firstName, string lastName, string location) :
this(firstName, lastName) => _location = location;
public virtual bool Equals(Person other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return FirstName == other.FirstName amp;amp; LastName == other.LastName;
}
public override int GetHashCode() => HashCode.Combine(FirstName, LastName);
}
затем тест проходит:
[TestMethod]
public void HashTest()
{
List<Person> people = new List<Person>()
{
new ("John", "Doe", "home"),
new ("John", "Doe", "Away"),
new ("Billy", "Doe", "home"),
new ("Billy", "Doe", "home"),
new ("Batty", "Bo", "home"),
};
var peopleHash = people.ToHashSet();
Assert.AreEqual(3, peopleHash.Count);
}
Комментарии:
1. В конце концов, независимо от того, как вы снимете шкуру с этого кота, вам всегда придется где-то переопределить Equals и получить код. Однако мне кажется, что делать это в самой записи удобнее.
2. Да, я согласен, но для меня тип записи был бы более полезен, если бы он создавал эти методы, основанные только на неизменяемых свойствах, описанных: Лицо с публичной записью(имя строки, фамилия строки)
3. @John Вы можете отправить билет с предложением по адресу github.com/dotnet/csharplang
Ответ №2:
Используя IEqualityComparer
вот так
using System;
using System.Collections.Generic;
using System.Linq;
public static class TestRecordTypes
{
public static void Main()
{
List<Person> people = new List<Person>()
{
new Person("John", "Doe", "home"),
new Person("John", "Doe", "Away"),
new Person("Billy", "Doe", "home"),
new Person("Billy", "Doe", "home"),
new Person("Batty", "Bo", "home"),
};
var peopleHash = people.ToHashSet(new MyEqualityComparer());
Console.WriteLine(peopleHash.Count());
}
}
public record Person(string FirstName, string LastName)
{
string _location;
public Person(string firstName, string lastName, string location):
this(firstName, lastName)
{
_location = location;
}
}
class MyEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person p1, Person p2)
{
if (p1 == null amp;amp; p2 == null)
return true;
else if (p1 == null || p2 == null)
return false;
else if(p1.FirstName == p2.FirstName amp;amp; p1.LastName == p2.LastName)
return true;
else
return false;
}
public int GetHashCode(Person p)
{
string s = p.FirstName p.LastName;
return s.GetHashCode();
}
}
Комментарии:
1. Спасибо, это работает наверняка, но, на мой взгляд, не имеет смысла использовать запись в первом случае. Так как это будет работать и для структуры/класса
2. Как задокументировано корпорацией Майкрософт: «Мы рекомендуем использовать класс EqualityComparer<T> вместо реализации интерфейса IEqualityComparer<T><T>, поскольку класс EqualityComparer<T><T> проверяет равенство с помощью метода IEquatable<T><T>.Equals вместо метода Object.Equals. »
3. @TimSchmelter Я должен признать, что для меня это тоже не имело большого смысла, но с Microsoft никогда не знаешь наверняка.