Как я могу создать свойство ObservableCollection только для чтения?
Я хотел бы показать свойство на модели представления, которая содержит список объектов (из базы данных).
Мне нужна эта коллекция для чтения. То есть я хочу предотвратить добавление/удаление и т.д. Но разрешите работу foreach и indexers. Мое намерение состоит в том, чтобы объявить частное поле, в котором находится редактируемая коллекция, и ссылаться на него с общедоступным объектом только для чтения. Как следует
public ObservableCollection<foo> CollectionOfFoo {
get {
return _CollectionOfFoo;
}
}
Однако этот синтаксис просто препятствует изменению ссылки на коллекцию. Это не препятствует добавлению/удалению и т.д.
Каков правильный способ выполнить это?
Ответы
Ответ 1
Принимаемый ответ [ранее] фактически возвращает другой ReadOnlyObservableCollection каждый раз, когда обращается к ReadOnlyFoo. Это расточительно и может привести к тонким ошибкам.
Предпочтительным решением является:
public class Source
{
Source()
{
m_collection = new ObservableCollection<int>();
m_collectionReadOnly = new ReadOnlyObservableCollection<int>(m_collection);
}
public ReadOnlyObservableCollection<int> Items
{
get { return m_collectionReadOnly; }
}
readonly ObservableCollection<int> m_collection;
readonly ReadOnlyObservableCollection<int> m_collectionReadOnly;
}
Для полного обсуждения см. ReadOnlyObservableCollection anti-pattern.
Ответ 2
Мне не нравится использовать ReadOnlyObservableCollection<T>
, поскольку это кажется ошибкой/сломанным классом; Вместо этого я предпочитаю подход, основанный на контрактах.
Вот что я использую, что позволяет ковариацию:
public interface INotifyCollection<T>
: ICollection<T>,
INotifyCollectionChanged
{}
public interface IReadOnlyNotifyCollection<out T>
: IReadOnlyCollection<T>,
INotifyCollectionChanged
{}
public class NotifyCollection<T>
: ObservableCollection<T>,
INotifyCollection<T>,
IReadOnlyNotifyCollection<T>
{}
public class Program
{
private static void Main(string[] args)
{
var full = new NotifyCollection<string>();
var readOnlyAccess = (IReadOnlyCollection<string>) full;
var readOnlyNotifyOfChange = (IReadOnlyNotifyCollection<string>) full;
//Covarience
var readOnlyListWithChanges =
new List<IReadOnlyNotifyCollection<object>>()
{
new NotifyCollection<object>(),
new NotifyCollection<string>(),
};
}
}
Ответ 3
Вы можете изменить тип своего свойства как IEnumerable:
public IEnumerable<foo> CollectionOfFoo {
get {
return _CollectionOfFoo;
}
}
Я не верю, что существует стандартный интерфейс, который предоставляет индекс. Если вам это нужно, вы можете написать интерфейс и расширить ObservableCollection для его реализации:
public interface IIndexerCollection<T> : IEnumerable<T>
{
T this[int i]
{
get;
}
}
public class IndexCollection<T> : ObservableCollection<T>, IIndexerCollection<T>
{
}
Ответ 4
Вы также можете переопределить класс списка, который вы используете, и поместить неизменяемый флаг в один из конструкторов, чтобы он не добавлял/не удалял, если он был создан с неизменным флагом, установленным в true.
Ответ 5
Используйте ReadOnlyObservableCollection <T>
public ReadOnlyObservableCollection<T> ReadOnlyFoo
{
get { return new ReadOnlyObservableCollection<T> (_CollectionOfFoo); }
}
Как уже указывалось, используйте Eric J, так как это ошибочно возвращает новый экземпляр каждый раз.