如何创建一个只读的观察汇总属性?

时间:2022-03-24 18:58:02

I'd like to expose a property on a view model that contains a list of objects (from database).

我想在包含对象列表(来自数据库)的视图模型上公开一个属性。

I need this collection to be read-only. That is, I want to prevent Add/Remove, etc. But allow the foreach and indexers to work. My intent is to declare a private field holding the editable collection and reference it with a read-only Public Property. As follows

我需要这个集合是只读的。也就是说,我想防止添加/删除等等。但是允许foreach和索引器工作。我的意图是声明一个包含可编辑集合的私有字段,并使用只读公共属性引用它。如下

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

However, that syntax just prevents changing the reference to the collection. It doesn't prevent add/remove, etc.

但是,这种语法只会阻止对集合的引用的更改。它不阻止添加/删除等。

What is the right way to accomplish this?

什么是实现这一目标的正确方法?

5 个解决方案

#1


57  

The [previously] accepted answer will actually return a different ReadOnlyObservableCollection every time ReadOnlyFoo is accessed. This is wasteful and can lead to subtle bugs.

[先前]接受的答案实际上会在每次访问ReadOnlyFoo时返回不同的ReadOnlyObservableCollection。这是一种浪费,可能会导致细微的错误。

A preferable solution is:

一个更可取的措施是:

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;
}

See ReadOnlyObservableCollection anti-pattern for a full discussion.

详细讨论请参阅ReadOnlyObservableCollection反模式。

#2


7  

I don't like using ReadOnlyObservableCollection<T> as it seems like a mistake / broken class; I prefer a contract based approach instead.

我不喜欢使用ReadOnlyObservableCollection ,因为它看起来像是一个错误的类;我更喜欢基于合同的方法。

Here is what I use that allows for covarience:

下面是我使用的考虑协变的方法:

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


0  

You could change the type of your property to be an IEnumerable:

您可以将您的属性类型更改为IEnumerable:

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

I don't believe there is a standard interface that exposes an indexer. If you need it you could write an interface and extend ObservableCollection to implement it:

我不相信有一个公开索引器的标准接口。如果您需要它,您可以编写一个接口并扩展ObservableCollection来实现它:

public interface IIndexerCollection<T> : IEnumerable<T>
{
    T this[int i]
    {
        get;
    }
}

public class IndexCollection<T> : ObservableCollection<T>, IIndexerCollection<T>
{
}

#4


-1  

You could also override the list class that you're using and put an immutable flag in one of the constructors such that it will not add/remove if it was constructed with the immutable flag set to true.

您还可以重写正在使用的list类,并在其中一个构造函数中放置一个不可变标志,这样,如果它是用设置为true的不可变标志构造的,它就不会添加/删除。

#5


-1  

Use ReadOnlyObservableCollection< T >

使用ReadOnlyObservableCollection < T >

public ReadOnlyObservableCollection<T> ReadOnlyFoo
{
    get { return new ReadOnlyObservableCollection<T> (_CollectionOfFoo); }
}

As has been pointed out, please use Eric J's answer as this one mistakenly is returning a new instance every time.

正如已经指出的,请使用Eric J的答案,因为这个错误每次都返回一个新的实例。

#1


57  

The [previously] accepted answer will actually return a different ReadOnlyObservableCollection every time ReadOnlyFoo is accessed. This is wasteful and can lead to subtle bugs.

[先前]接受的答案实际上会在每次访问ReadOnlyFoo时返回不同的ReadOnlyObservableCollection。这是一种浪费,可能会导致细微的错误。

A preferable solution is:

一个更可取的措施是:

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;
}

See ReadOnlyObservableCollection anti-pattern for a full discussion.

详细讨论请参阅ReadOnlyObservableCollection反模式。

#2


7  

I don't like using ReadOnlyObservableCollection<T> as it seems like a mistake / broken class; I prefer a contract based approach instead.

我不喜欢使用ReadOnlyObservableCollection ,因为它看起来像是一个错误的类;我更喜欢基于合同的方法。

Here is what I use that allows for covarience:

下面是我使用的考虑协变的方法:

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


0  

You could change the type of your property to be an IEnumerable:

您可以将您的属性类型更改为IEnumerable:

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

I don't believe there is a standard interface that exposes an indexer. If you need it you could write an interface and extend ObservableCollection to implement it:

我不相信有一个公开索引器的标准接口。如果您需要它,您可以编写一个接口并扩展ObservableCollection来实现它:

public interface IIndexerCollection<T> : IEnumerable<T>
{
    T this[int i]
    {
        get;
    }
}

public class IndexCollection<T> : ObservableCollection<T>, IIndexerCollection<T>
{
}

#4


-1  

You could also override the list class that you're using and put an immutable flag in one of the constructors such that it will not add/remove if it was constructed with the immutable flag set to true.

您还可以重写正在使用的list类,并在其中一个构造函数中放置一个不可变标志,这样,如果它是用设置为true的不可变标志构造的,它就不会添加/删除。

#5


-1  

Use ReadOnlyObservableCollection< T >

使用ReadOnlyObservableCollection < T >

public ReadOnlyObservableCollection<T> ReadOnlyFoo
{
    get { return new ReadOnlyObservableCollection<T> (_CollectionOfFoo); }
}

As has been pointed out, please use Eric J's answer as this one mistakenly is returning a new instance every time.

正如已经指出的,请使用Eric J的答案,因为这个错误每次都返回一个新的实例。