Страницы

Поиск по вопросам

вторник, 9 июля 2019 г.

Наследование generic-класса от интерфейса

У меня есть классы и интерфейсы:
public interface IEntity { int ID { get; set; } }
public class Entity : IEntity { public int ID { get; set; } }
public interface IEntityListViewModel { RangeObservableCollection Items { get; set; } IEntity SelectedItem { get; set; }
void LoadItems(); }
Теперь мне нужен такой класс:
public abstract class EntityListViewModel : IEntityListViewModel where T : IEntity { public RangeObservableCollection Items { get; set; } public T SelectedItem { get; set; }
public EntityListViewModel() { Items = new RangeObservableCollection(); }
protected abstract List GetEntities();
public void LoadItems() { var lst = GetEntities(); Items.ReplaceRange(lst); } }
Естественно, компилятор требует реализации RangeObservableCollection.Items
Я бы мог сделать так:
public interface IEntityListViewModel where T : IEntity { RangeObservableCollection Items { get; set; } T SelectedItem { get; set; }
void LoadItems(); }
Но у меня есть еще один класс:
public abstract class UserControlBase : UserControl { public IEntityListViewModel VM { get; set; }
public virtual void Page_Loaded(object sender, RoutedEventArgs e) { VM.LoadItems(); } }
А дальше уже конкретные UserControl'ы наследуются от UserControlBase. Для чего это мне? В UserControl'ах куча повторяющегося кода (в основном привязки событий ViewModel). Поэтому это все я хочу сделать в UserControlBase. Я бы мог объявить в UserControlBase так:
public IEntityListViewModel VM { get; set; }
но тогда мне T нужно поднимать в UserControlBase, а это как-то криво.
Как мне наследовать EntityListViewModel от обычного IEntityListViewModel? Или есть какое-то другое решение?
UPD 1: Явно реализовал, как посоветовали в комментариях:
public abstract class EntityListViewModel : IEntityListViewModel where T : IEntity { public RangeObservableCollection Items { get; set; } public T SelectedItem { get; set; } RangeObservableCollection IEntityListViewModel.Items { get; set; } IEntity IEntityListViewModel.SelectedItem { get; set; }
public EntityListViewModel() { Items = new RangeObservableCollection(); }
protected abstract List GetEntities();
public void LoadItems() { var lst = GetEntities(); Items.ReplaceRange(lst); } }
Но теперь во View вызов VM.Items обращается к IEntityListViewModel.Items


Ответ

В текущем виде самым простым для вас было бы действительно объявить UserControlBase обобщенным. Если же этот способ вам по какой-то причине не подойдет - надо выносить хранение из базового класса в наследники.
В вашем случае, если базовый класс подписывается на события - ему нужен интерфейс INotifyCollectionChanged. Его и надо запрашивать у наследника:
public abstract class UserControlBase : UserControl { protected abstract INotifyCollectionChanged AbstractItems { get; } }
public class FooListControl : UserControlBase { public IEntityListViewModel VM { get; set; }
protected virtual INotifyCollectionChanged AbstractItems => VM.Items; }
Если при таком подходе в базовом классе оказывается слишком много полей - надо выделять в интерфейсе ковариантную часть.
Например, для ваш интерфейс IEntityListViewModel можно разбить следующим образом:
public interface IEntityListViewModel : IEntityListViewModelOut where T : IEntity { RangeObservableCollection Items { get; set; } T SelectedItem { get; set; } }
public interface IEntityListViewModelOut where T : IEntity { IReadOnlyList Items { get; } INotifyCollectionChanged ItemsEvents { get; } T SelectedItem { get; }
void LoadItems(); }
Реализовать такой "усложненный" интерфейс не сильно сложнее чем обычный:
public class EntityListViewModel : IEntityListViewModel where T : IEntity { public RangeObservableCollection Items { get; set; } = new RangeObservableCollection(); public T SelectedItem { get; set; }
IEntityListViewModelOut.Items => Items; IEntityListViewModelOut.ItemsEvents => Items; IEntityListViewModelOut.SelectedItem => SelectedItem; }
После этого, ковариантную часть можно "пробросить" в базовый класс через абстрактное свойство:
public abstract class UserControlBase : UserControl { protected abstract IEntityListViewModelOut AbstractVM { get; } }
public class FooListControl : UserControlBase { public IEntityListViewModel VM { get; set; }
protected virtual IEntityListViewModelOut AbstractVM => VM; }

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

Отправить комментарий