Преобразование хэш-наборов в C#

#c# #.net #.net-4.0

#c# #.net #.net-4.0

Вопрос:

У меня есть два класса, где один является производным от другого. У меня также есть поле HashSet, в котором хранится множество производных классов. Проблема в том, что производный класс используется только внутри, и у меня есть свойство, которое должно возвращать хэш-набор базовых классов. Я знаю, что у List есть метод ConvertAll, есть ли у HashSet что-то подобное?

 public class Base
{
}

public class MyClass
{
    private class Derived : Base
    {
    }

    private HashSet<Derived> mList;

    public HashSet<Base> GetList { get { /* Convert mList to HashSet<Base> */ } }
}
  

Ответ №1:

Вы не можете преобразовать a HashSet<Derived> в a HashSet<Base> без приведения каждого элемента.

Чтобы объяснить, почему это невозможно, взгляните на следующий пример кода:

 HashSet<Derived> mySet = ...;
HashSet<Base> x = (HashSet<Base>)mySet;  // imagine this were possible

x.Add(new Base());  // legal code, but cannot work, since x is really a HashSet<Derived>
  

Однако, поскольку интерфейс IEnumerable является ковариантным, должно быть возможным следующее:

 HashSet<Derived> mySet = ...;
IEnumerable<Base> x = mySet;  // works in C# 4

// no problem here, since no items can be added to an IEnumerable
  

Таким образом, вы могли бы изменить объявление вашего метода:

 public IEnumerable<Base> GetList { get { return mList; } }
  

Ответ №2:

Просто объявите mList как…

 private HashSet<Base> mList;
  

Вы можете сделать…

 public HashSet<Base> GetList { get { return new HashSet<Base>(mList); } }
  

Поскольку mList реализует IEnumerable<Base>

Комментарии:

1. Я задавался вопросом об этом, но, конечно, если он часто использует это внутренне, то потенциально требуется много приведения к производному. Возможно, было бы лучше просто выполнить метод ConvertAll вручную, чем разбрасывать приведения повсюду…

2. @Chris Я думаю, что это был бы неприятный запах кода, если бы это было так, но вы правы, это, безусловно, возможно.

3. Приведение на самом деле не обязательно. mList как у вас есть, это уже IEnumerable<Base> .

4. @Bencr: почему вы говорите, что это плохой запах кода? Предположительно, у него есть хэш-набор<Производный>, потому что он что-то делает с элементами в этом наборе? Использует ли он это много или мало — это другой вопрос, но я не могу представить, сколько он делает, это запах. Возможно, я что-то упускаю из виду, почему OP может использовать HashSet<Производный> …

5. @Крис, если его базовый класс был Car с void Drive() операцией, а его производный класс был SportsCar который переопределял Drive , тогда зачем приводить? Однако, если бы его производный класс был чем-то совершенно другим, что расширяло интерфейс, например, Plane : Car с помощью Fly() операции, тогда ему пришлось бы приводить, когда лучшим дизайном могло бы быть наличие базового класса TransportMethod с виртуальной Travel() операцией. Я не знаю точно, чего он пытается достичь, но я знаю, что мне редко приходится упаковывать и распаковывать объекты, поскольку были доступны дженерики.

Ответ №3:

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