Как фильтровать коллекцию объектов базового класса по типу созданного подкласса?

StackOverflow https://stackoverflow.com/questions/989338

Вопрос

Я написал этот пример, чтобы объяснить.Как видите, у меня есть иерархия объектов.Я хотел бы изменить функцию 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());
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top