Подтвердить что ты не робот

Как я могу создать свойство ObservableCollection только для чтения?

Я хотел бы показать свойство на модели представления, которая содержит список объектов (из базы данных).

Мне нужна эта коллекция для чтения. То есть я хочу предотвратить добавление/удаление и т.д. Но разрешите работу foreach и indexers. Мое намерение состоит в том, чтобы объявить частное поле, в котором находится редактируемая коллекция, и ссылаться на него с общедоступным объектом только для чтения. Как следует

public ObservableCollection<foo> CollectionOfFoo { 
     get { 
         return _CollectionOfFoo;
     }
}

Однако этот синтаксис просто препятствует изменению ссылки на коллекцию. Это не препятствует добавлению/удалению и т.д.

Каков правильный способ выполнить это?

4b9b3361

Ответ 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, так как это ошибочно возвращает новый экземпляр каждый раз.