2010-04-05 5 views
2

개인 멤버로 두 개의 HashSet<String> 컬렉션이있는 클래스가 있습니다. 내 코드의 다른 클래스는 해당 HashSet을 반복하고 해당 내용을 읽을 수 있기를 원합니다. 나는 다른 클래스가 여전히 비슷한 것을 할 수 있기 때문에 표준 getter를 작성하고 싶지 않다. myClass.getHashSet().Clear(); HashSet 자체에 대한 참조를 노출하지 않고 내 HashSet의 요소를 반복에 노출시키는 다른 방법이 있습니까? for-each 루프와 호환되는 방식으로이 작업을 수행하고 싶습니다.(C#) 읽기 전용 개인 모음 멤버를 반복합니다.

+0

감사합니다, 모두를 쓸 수 있습니다. 가장 완벽한 답을 얻기위한 스트 레거, 그리고 내 OP를 정리 한 Jon Skeet. – DGH

답변

3

IEnumerable<T> 재산 노출 : 그래서 (상처 동안 성능) 안전을 위해

public IEnumerable<whatevertype> MyHashSet { 
    get { 
     return this.myHashSet; 
    } 
} 

은 물론,이 코드의 사용자가 HashSet<T> 편집 요소가 IEnumerable<T> 캐스팅 할 수 있습니다, 당신은 할 수 수행

public IEnumerable<whatevertype> MyHashSet { 
    get { 
     return this.myHashSet.ToArray(); 
    } 
} 

나 :

public IEnumerable<whatevertype> MyHashSet { 
    get { 
     foreach(var item in this.myHashSet) { 
      yield return item; 
     } 
    } 
} 
public IEnumerator<whatevertype> GetMyHashSetEnumerator() { 
    return this.myHashSet.GetEnumerator(); 
} 
+0

왜 'ToArray'옵션을 제시할까요? iterator 블록은 더 성능이 좋고 추가 메모리를 소비하지 않습니다. (컴파일러 생성 스텁 클래스의 설정 이외에). –

+0

수율 접근 방식이 게으른 실행이고 더 많은 메모리를 사용해서는 안되기 때문에 가장 깨끗하다고 ​​생각합니다. 나는 그것을 측정하지 않았다. –

+0

흥미로운 질문 : 'yield return'이 ToArray()보다 빠르게 작동합니까? –

3

이 같은 방법/속성을 추가 실제 컨테이너를 노출하지 않도록 : 414,더 성능이 좋은 보호 방법,하지만 호출자에게 덜 편리한이 IEnumerator<T>가 반환하는 것입니다

public IEnumerable EnumerateFirst() 
{ 
    foreach(var item in hashSet) 
     yield return item; 
} 
+1

IEnumerable을 HashSet으로 다시 캐스팅하는 것을 막음으로써/진짜/불쾌한 호출자를 막을 수 있습니다. 그러나 IEnumerable을 강력하게 입력하는 것은 아닙니다. 그렇게 좋은 것은 아닙니다. –

+1

+1. 다른 사람들은 'IEnumerable '으로만 회원을 드러내는 것을지지하지만 (이는 엄격한 의미에서 원하는 것을 성취 할 것입니다.) 이는 안전한 관행이 아닙니다. 객체 자체가 인터페이스로만 노출되는 동안 진취적인 개발자가 객체를 'HashSet'으로 다시 캐스팅하는 것을 막을 수있는 방법은 없습니다. 실제'HashSet' 참조를 노출시키지 않으므로 여기 연습은 더 안전합니다. –

+0

...하지만 그것은 슬프게도, 슬프게도. – strager

-1

만들기 Getter는 HashSet을 IEnumerable로 노출합니다.

private HashSet<string> _mine; 

public IEnumerable<string> Yours 
{ 
    get { return _mine; } 
} 

제네릭 형식이 변경 가능한 경우

는, 그 여전히 수정할 수 있지만 아이템이 추가되지 또는 HashSet의에서 제거 할 수 있습니다.

public IEnumerable<string> GetHashSetOneValues() 
{ 
    foreach (string value in hashSetOne) 
     yield return value; 
} 
이 방법은 다음 foreach 루프 내에서 호출 할 수 있습니다

:

foreach (string value in myObject.GetHashSetOneValues()) 
    DoSomething(value); 
+1

예, 호출자가 'HashSet '으로 다시 캐스트해야합니다. –

+0

좋아요, 그렇다면 어떤면에서도 안전 보장은 없습니다. _mine이 비공개 인스턴스 멤버 인 경우에도 리플렉션을 사용하여 얻을 수 있습니다. 소비자가 API에서 제공하는 계약을 위반하는 경우, 그렇게함으로써 결과를 받아 들여야합니다. IEnumerable 으로 노출시킴으로써 소비자는 컬렉션을 수정하지 않고 반복해야한다는 것을 분명하게 알 수 있습니다. 그게 충분하지 않아? –

+0

일반적으로 사용되는 것이 아니라 일반적으로 사용됩니다. 캐스팅과 반사 기반 개인 회원 액세스에는 큰 차이가 있습니다. –

0

당신은 또한이 같은 일련의를 제공 할 수있다 HashSet<T> 다시 캐스팅 할 수 없습니다

public IEnumerable<int> Values 
{ 
    get { return _values.Select(value => value); 
} 

목 는 .ToArray()처럼 _values을 두 번 반복하지 않지만 구현을 단일 깨끗한 선으로 유지합니다.

3

당신은 또한보다 랩퍼를 작성하기 위해 Select 방법을 사용할 수 있습니다

+1

위대한 마음은 비슷하게 생각합니다 :) –

+0

음, 재미 있습니다 ... – strager

6

.NET 3.5를 사용한다고 가정하면 yield 코드를 직접 작성하는 대신 LINQ 메소드를 호출 할 수 있습니다. 예를 들어 :

public IEnumerable<string> HashSet 
{ 
    get { return privateMember.Select(x => x); } 
} 

또는

public IEnumerable<string> HashSet 
{ 
    get { return privateMember.Skip(0); } 
} 

과 같이 사용할 수있는 다양한 LINQ 사업자가 있습니다 - 초기 이후로, Skip(0) 아마 가장 효율적으로 사용하여 "0 값을 생략"루프, 그것은이다 아마도 foreach/yield return 루프가 다른 답변에 표시됩니다. Select 버전은 각 항목에 대해 no-op 투영 대리자를 호출합니다.이 차이가 중요 할 확률은 천문학적으로 작습니다. - 코드를 가장 명확하게 만드는 것은 무엇이든 함께하는 것이 좋습니다.

+0

+1. 불필요한 람다 오버 헤드를 피하기 위해'Skip'을 사용할 것입니다. 굉장히 실용적이지는 않지만 실제로 여기서 당신을 얻지는 않습니다. –

+0

+1; '건너 뛰기 (0)'는 좋은 생각입니다! – strager

0

이것은 파티에 너무 늦을 지 모르지만 오늘날 가장 쉬운 방법은 Linq를 사용하는 것입니다. 대신

public IEnumerable<string> GetValues() 
{ 
    foreach(var elem in list) 
     yield return elem; 
} 

을 쓰는 당신은 ESP,

public IEnumerable<string> GetValues() => list;