Как фильтровать коллекцию объектов базового класса по типу созданного подкласса?
-
13-09-2019 - |
Вопрос
Я написал этот пример, чтобы объяснить.Как видите, у меня есть иерархия объектов.Я хотел бы изменить функцию GetFeatures(), чтобы она возвращала только функции, добавленные конструктором типа объекта, который я создал.Например, BasicModel.GetFeatures(new LuxuryModel()) должен возвращать только функции «Кожаные сиденья» и «Люк на крыше».Я не против использовать отражение, если придется.
Public Class Feature
Public Sub New(ByVal model As BasicModel, ByVal description As String)
_model = model
_description = description
End Sub
Private _model As BasicModel
Public Property Model() As BasicModel
Get
Return _model
End Get
Set(ByVal value As BasicModel)
_model = value
End Set
End Property
Private _description As String
Public Property Description() As String
Get
Return _description
End Get
Set(ByVal value As String)
_description = value
End Set
End Property
End Class
Public Class BasicModel
Public Sub New()
_features = New List(Of Feature)
End Sub
Private _features As List(Of Feature)
Public ReadOnly Property Features() As List(Of Feature)
Get
Return _features
End Get
End Property
Public Shared Function GetFeatures(ByVal model As BasicModel) As List(Of Feature)
'I know this is wrong, but something like this...'
Return model.Features.FindAll(Function(f) f.Model.GetType() Is model.GetType())
End Function
End Class
Public Class SedanModel
Inherits BasicModel
Public Sub New()
MyBase.New()
Features.Add(New Feature(Me, "Fuzzy Dice"))
Features.Add(New Feature(Me, "Tree Air Freshener"))
End Sub
End Class
Public Class LuxuryModel
Inherits SedanModel
Public Sub New()
MyBase.New()
Features.Add(New Feature(Me, "Leather Seats"))
Features.Add(New Feature(Me, "Sunroof"))
End Sub
End Class
Решение
Если вы хотите сохранить текущую иерархию/свойства, просто создайте экземпляр базового класса и вычтите функции базового класса из функций производного класса.
В качестве альтернативы вы можете рассмотреть возможность немного изменить иерархию классов, чтобы облегчить себе задачу.
Например, вы можете разделить свойство Features на два свойства, скажем ModelFeatures и AllFeatures.
ModelFeatures относится к текущей модели («Кожаные сиденья» и «Люк на крыше» для LuxuryModel и т. д.). AllFeatures возвращает объединение MyBase.AllFeatures и ModelFeatures.Это делает получение функций текущей модели тривиальным доступом к свойствам.
P.S.Пожалуйста, простите мои ошибки VB, я предпочитаю C#.
Другие советы
Я не совсем знаком с синтаксисом VB.NET, поэтому вот синтаксис C#, который должен работать:
public IList<Function> GetFeatures(BasicModel model)
{
return model.Features.Where(f => f.Model.GetType() == model.GetType()).ToList();
}
(на основе вашего примера кода выше)
Честно говоря, я не думаю, что это относится к вашему Car Heirachy, мне кажется, что вы хотите получить статические данные и использовать свой базовый класс в качестве контейнера для них с вашими дочерними типами в качестве ключа.Как вы можете видеть, это приводит к неуклюжему или чрезмерно сложному кодированию чего-то простого.
Я был бы гораздо более склонен поместить ваши статические данные туда, где они должны быть (в данном случае) с самим классом Feature.Я не разбираюсь в VB, поэтому этот фрагмент кода на C#.
Итак, в самом классе Feature есть статический свойство (или метод)
public static IEnumerable<Feature> GetLuxuryFeatureSets
{
get
{
yield return new Feature() { Name = "Leather Seats" };
yield return new Feature() { Name = "Sunroof" };
}
}
И повторите для других ваших моделей.Итак, теперь у вас есть контейнер для статических данных, который имеет смысл, — сам класс.
Поскольку невозможно отличить одну функцию от другой, вам нужно иметь возможность спросить Модель, какие функции она добавила.Вместо того, чтобы добавлять функции в список в вашем конструкторе, у вас должен быть метод, который возвращает список функций, которые добавляет конкретная модель...
public class LuxuryModel
{
public LuxuryModel
{
Features.Add( GetFeatures() );
}
public List<Features> GetFeatures()
{
return new List<Features>( /* new up leather and whatever */
}
}
Затем GetFeatures может выполнить понижение и запросить у экземпляра список функций, которые добавляет этот конкретный экземпляр...
public List<Features> GetFeatures<T>( BasicModel model )
{
var m = Model as T;
return m.GetFeatures();
}
Другой способ сделать это, если вы не хотите, чтобы эти данные были статическими (см. другой мой ответ), и вы хотите, чтобы их можно было расширять на новые классы и чтобы эти классы могли управлять наборами функций.Затем используйте полиморфизм (опять же C#, так как я не парень VB)
В вашем базовом классе создайте виртуальный метод, который возвращает функции, в ваших достойных классах переопределите это свойство, полиморфизм позволяет вашим переменным типа базовой модели, если они были созданы как тип-потомок, возвращать правильный список.
public class BasicModel
{
public virtual IEnumerable<Feature> GetFeatures
{
get
{
throw new NotImplementedException();
}
}
}
public class LuxuryModel :BasicModel
{
public override IEnumerable<Feature> GetFeatures
{
get
{
yield return new Feature() { Name = "Leather Seats" };
yield return new Feature() { Name = "Sunroof" };
}
}
}
private void button1_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
BasicModel bm = new LuxuryModel();
foreach (Feature f in bm.GetFeatures)
{
sb.AppendLine(f.Name);
}
MessageBox.Show(sb.ToString());
}