#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
из старого. Они не будут синхронизированы.