ReadOnlyObservableCollection.CollectionChanged가 공개되지 않는 이유는 무엇입니까?


88

ReadOnlyObservableCollection.CollectionChanged보호되고 공개되지 않는 이유는 무엇입니까 (해당하는대로 ObservableCollection.CollectionChanged)?

이벤트에 INotifyCollectionChanged액세스 할 수없는 경우 구현하는 컬렉션의 용도는 무엇입니까 CollectionChanged?


1
궁금해서 읽기 전용 컬렉션 변경 될 것으로 예상하는 이유는 무엇입니까? 그렇다면 읽기 전용이 아닐까요?
workmad3

84
반대 질문 : ObservableCollection이 변경되지 않는 이유는 무엇입니까? 아무것도 변하지 않을 때 그것을 관찰하는 것은 무엇입니까? 글쎄요, 컬렉션은 확실히 바뀔 것입니다.하지만 저는 그것을 관찰 할 수있는 소비자가 있습니다. 보고되지하지만 감동 ...
오스카

1
나는 최근에이 문제를 발견했습니다. 기본적으로 ObservableCollection은 INotifyCollection 변경 이벤트를 제대로 구현하지 않습니다. C #에서 클래스가 인터페이스 메서드가 아닌 액세스 인터페이스 이벤트를 제한하도록 허용하는 이유는 무엇입니까?
djskinner

36
나는 이것이 완전한 광기라는 것에 대한 나의 투표에 넣어야한다. CollectionChanged 이벤트를 구독 할 수 없는데도 왜 ReadOnlyObservableCollection이 존재합니까? WTF가 요점입니까? 그리고 읽기 전용 컬렉션은 결코 변하지 않을 것이라고 계속 말하는 모든 사람들에게 당신이 말하는 것에 대해 정말 열심히 생각하십시오.
MojoFilter

9
"읽기 전용"은 일부 사람들이 생각하는 것처럼 "불변"을 의미하지 않습니다. 이러한 속성을 통해서만 컬렉션을 볼 수있는 코드는 변경할 수 없다는 의미 일뿐입니다. 코드가 기본 컬렉션을 통해 멤버를 추가하거나 제거 할 때 실제로 변경 될 수 있습니다. 독자는 컬렉션 자체를 변경할 수없는 경우에도 변경 사항이 발생했음을 알릴 수 있어야합니다. 그들은 여전히 ​​스스로 변화에 대응해야 할 수도 있습니다. 이 경우에 수행 된 것처럼 CollectionChanged 속성을 제한 할 유효한 이유가 없다고 생각할 수 있습니다.
Gil

답변:



16

이 작업을 수행하는 방법을 찾았습니다.

ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);

INotifyCollectionChanged 인터페이스에서 명시 적으로 컬렉션을 참조하면 됩니다.


14
ReadOnlyObservableCollection이 ObservableCollection을 둘러싼 래퍼라는 점을 유의하십시오. 변경 될 것입니다. ReadOnlyObservableCollection의 소비자는 이러한 변경 사항을 관찰 만 할 수 있으며 스스로 변경할 수 없습니다.
Oskar

이 사실에 의존해서는 안됩니다. 읽기 전용 컬렉션은 어떤 경우에도 변경되지 않을 것입니다. "읽기 전용"이라고 불리기 때문입니다. 이것은 나의 이해입니다.
Restuta

4
캐스팅 솔루션은 +1입니다. 그러나 읽기 전용 컬렉션을 관찰하는 것은 논리적이지 않습니다. 데이터베이스에 대한 읽기 전용 액세스 권한이 있다면 변경되지 않을 것으로 예상 할 수 있습니까?
djskinner

17
나는 여기서 어려움이 무엇인지 모르겠다. ReadOnlyObservableCollection은 컬렉션을 관찰하는 읽기 전용 방법을 제공하는 클래스입니다. 내 수업에 세션 모음이 있고 사람들이 내 모음에서 세션을 추가하거나 제거 할 수는 없지만 계속 관찰하는 것을 원하지 않는다면 ReadOnlyObservableCollection이이를위한 완벽한 수단입니다. 컬렉션은 내 수업의 사용자 만 읽을 수 있지만 내가 사용하기위한 읽기 / 쓰기 사본이 있습니다.
scwagner

1
의 닷넷 코드를 찾아 ReadOnlyObservableCollection당신은이를 찾을 것입니다 event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged. 이벤트의 명시 적 인터페이스 구현.
Mike de Klerk

7

이 게시물은 오래되었지만 사람들은 주석을 달기 전에 .NET에서 사용되는 패턴을 이해하는 데 시간을 투자해야합니다. 읽기 전용 컬렉션은 소비자가 직접 수정하지 못하도록하는 기존 컬렉션의 래퍼입니다. 살펴보면 변경 가능할 수도 있고 불가능할 수도 ReadOnlyCollection있는 래퍼임을 알 IList<T>수 있습니다. 불변 컬렉션은 다른 문제이며 새로운 불변 컬렉션 라이브러리 에서 다룹니다.

즉, 읽기 전용은 불변과 동일하지 않습니다 !!!!

제쳐두고 ReadOnlyObservableCollection암시 적으로 구현해야합니다 INotifyCollectionChanged.


5

ReadOnlyObservableCollection 에 대한 컬렉션 변경 알림을 구독하려는 데에는 확실히 좋은 이유가 있습니다 . 따라서 단순히 컬렉션을 INotifyCollectionChanged 로 캐스팅하는 대신 ReadOnlyObservableCollection을 서브 클래 싱 하는 경우 다음은 CollectionChanged 이벤트 에 액세스하는보다 구문 상 편리한 방법을 제공합니다 .

    public class ReadOnlyObservableCollectionWithCollectionChangeNotifications<T> : ReadOnlyObservableCollection<T>
{
    public ReadOnlyObservableCollectionWithCollectionChangeNotifications(ObservableCollection<T> list)
        : base(list)
    {
    }

    event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged2
    {
        add { CollectionChanged += value; }
        remove { CollectionChanged -= value; }
    }
}

이것은 이전에 잘 작동했습니다.


5

이 문제를 설명하는 Microsoft Connect의 버그 항목에 투표 할 수 있습니다 : https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

최신 정보:

Connect 포털은 Microsoft에 의해 종료되었습니다. 따라서 위의 링크는 더 이상 작동하지 않습니다.

My Win Application Framework (WAF) 라이브러리는 다음과 같은 솔루션을 제공합니다. ReadOnlyObservableList 클래스 :

public class ReadOnlyObservableList<T> 
        : ReadOnlyObservableCollection<T>, IReadOnlyObservableList<T>
{
    public ReadOnlyObservableList(ObservableCollection<T> list)
        : base(list)
    {
    }

    public new event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add { base.CollectionChanged += value; }
        remove { base.CollectionChanged -= value; }
    }

    public new event PropertyChangedEventHandler PropertyChanged
    {
        add { base.PropertyChanged += value; }
        remove { base.PropertyChanged -= value; }
    }
}

Connect가 사용 중지되었습니다. 나는 완전히 투표했을 것입니다
findusl

1

이미 대답했듯이 두 가지 옵션이 있습니다. 명시 적으로 구현 된 이벤트 에 액세스 ReadOnlyObservableCollection<T>하기 위해 인터페이스 INotifyCollectionChanged로 캐스트 CollectionChanged하거나 생성자에서 한 번 수행하고 래핑 된 이벤트를 연결하는 자체 래퍼 클래스를 만들 수 있습니다.ReadOnlyObservableCollection<T> .

이 문제가 아직 수정되지 않은 이유에 대한 추가 정보 :

당신은에서 볼 수 있듯이 , 소스 코드 , ReadOnlyObservableCollection<T>이벤트가 표시됩니다 공개, 밀봉되지 않은 (즉, 상속) 클래스이다 protected virtual.

즉, ReadOnlyObservableCollection<T>재정의 된 이벤트 정의는 있지만 protected가시성을 갖는 에서 파생 된 클래스가있는 컴파일 된 프로그램이있을 수 있습니다 . 이벤트의 가시성이 다음과 같이 변경되면 해당 프로그램에 잘못된 코드가 포함됩니다.public 파생 클래스에서 이벤트의 가시성을 제한 할 수 없기 때문에 기본 클래스에서 .

그래서 안타깝게도 나중에 protected virtual이벤트를 만드는 public것은 바이너리 브레이킹 변경이므로 아주 좋은 추론 없이는 수행되지 않을 것입니다. "핸들러를 연결하려면 객체를 한 번 캐스팅해야합니다"는 그렇지 않습니다.

출처 : Nick Guererra의 GitHub 댓글, 2015 년 8 월 19 일


0

이것은 Google에서 최고 인기를 얻었으므로 다른 사람들이 이것을 찾을 경우에 대비하여 솔루션을 추가 할 것이라고 생각했습니다.

위의 정보 ( INotifyCollectionChanged 로 캐스트해야 함 )를 사용하여 등록 및 등록 취소를위한 두 가지 확장 메서드를 만들었습니다.

내 솔루션 -확장 방법

public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged += handler;
}

public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged -= handler;
}

IThing.cs

public interface IThing
{
    string Name { get; }
    ReadOnlyObservableCollection<int> Values { get; }
}

확장 방법 사용

public void AddThing(IThing thing)
{
    //...
    thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged);
}

public void RemoveThing(IThing thing)
{
    //...
    thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged);
}

OP의 솔루션

public void AddThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged -= this.HandleThingCollectionChanged;
}

대안 2

public void AddThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged -= this.HandleThingCollectionChanged;
}

0

해결책

ReadOnlyObservableCollection.CollectionChanged 노출되지 않았으므로 (다른 답변에 설명 된 유효한 이유 때문에) 노출하는 자체 래퍼 클래스를 만들어 보겠습니다.

/// <summary>A wrapped <see cref="ReadOnlyObservableCollection{T}"/> that exposes the internal <see cref="CollectionChanged"/>"/>.</summary>
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
    public new NotifyCollectionChangedEventHandler CollectionChanged;

    public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { /* nada */ }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) => 
        CollectionChanged?.Invoke(this, args);
}

설명

사람들은 왜 읽기 전용 컬렉션의 변경 사항을 관찰해야하는지 물었으므로 여러 유효한 상황 중 하나를 설명하겠습니다. 읽기 전용 컬렉션이 변경할 수있는 개인 내부 컬렉션을 래핑하는 경우

다음은 그러한 시나리오 중 하나입니다.

서비스 외부에서 내부 컬렉션에 항목을 추가 및 제거 할 수있는 서비스가 있다고 가정합니다. 이제 컬렉션의 값을 노출하고 싶지만 소비자가 컬렉션을 직접 조작하는 것을 원하지 않는다고 가정합니다. 그래서 내부 컬렉션을 ReadOnlyObservableCollection.

내부 컬렉션을 내부 컬렉션으로 래핑하려면 의 생성자 ReadOnlyObservableCollection에서 파생 ObservableCollection되어야합니다 ReadOnlyObservableCollection.

이제 내부 콜렉션이 변경 될 때 (따라서 노출 된 ReadOnlyObservableCollection변경 사항 이있을 때) 서비스 소비자에게 알리려고한다고 가정하십시오 . 오히려 자신의 구현을 압연보다 그냥 노출 할 CollectionChanged의를 ReadOnlyObservableCollection. 소비자가의 구현에 대해 가정하도록 강요하는 대신 을이 사용자 정의로 ReadOnlyObservableCollection바꾸기 만하면됩니다.ReadOnlyObservableCollectionObservableReadOnlyCollection 완료됩니다.

은 자체적으로 ObservableReadOnlyCollection숨겨 ReadOnlyObservableCollection.CollectionChanged지며 모든 컬렉션 변경 이벤트를 연결된 이벤트 처리기에 전달합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.