У меня есть класс, который имеет две коллекции HashSet<String>
в качестве частных членов. Другие классы в моем коде хотели бы иметь возможность перебирать эти HashSets и читать их содержимое. Я не хочу писать стандартный getter, потому что другой класс все равно может сделать что-то вроде myClass.getHashSet().Clear();
Есть ли другой способ выставить элементы моих HashSets на итерацию, не подвергая ссылку на сам HashSet? Мне бы хотелось, чтобы я мог сделать это таким образом, который совместим с каждым циклом.(C#) итерация по частному члену частной коллекции только для чтения
ответ
Выставьте IEnumerable<T>
свойства:
public IEnumerable<whatevertype> MyHashSet {
get {
return this.myHashSet;
}
}
Конечно, пользователь этого кода может бросить эту IEnumerable<T>
до HashSet<T>
и редактирования элементов, так, чтобы быть на безопасной стороне (в то время как отрицательно скажется на производительность), вы можете делать:
public IEnumerable<whatevertype> MyHashSet {
get {
return this.myHashSet.ToArray();
}
}
или:
public IEnumerable<whatevertype> MyHashSet {
get {
foreach(var item in this.myHashSet) {
yield return item;
}
}
}
Более производительный метод защиты, но менее удобно для абонента, чтобы возвращать IEnumerator<T>
:
public IEnumerator<whatevertype> GetMyHashSetEnumerator() {
return this.myHashSet.GetEnumerator();
}
Зачем даже предлагать опцию 'ToArray'? Блок итератора более эффективен и не потребляет дополнительную память (кроме настройки для создаваемого класса-заглушки, созданного компилятором.) –
Я думаю, что подход доходности чист, поскольку он ленивый и не должен использовать больше памяти. Я его не измерил. –
Интересный вопрос: работает ли 'return return' быстрее, чем' ToArray() '? –
Добавить метод/свойство так, чтобы не подвергать фактическую емкость:
public IEnumerable EnumerateFirst()
{
foreach(var item in hashSet)
yield return item;
}
Лучше, так как это останавливает/действительно/отвратительных вызывающих лиц, бросающих IEnumerable обратно в HashSet. Однако вы не сильно набираете IEnumerable, что, возможно, не так хорошо. –
+1. В то время как другие выступают за то, чтобы обнародовать участника только как «IEnumerable
... но это медленнее, грустно. – strager
Марка ваш геттер выставляет HashSet как IEnumerable.
private HashSet<string> _mine;
public IEnumerable<string> Yours
{
get { return _mine; }
}
Если общий тип изменчиво, то, что все еще может быть изменен, но никакие пункты не могут быть добавлены или удалены из HashSet.
Да, они могут - вызывающий абонент просто должен вернуть обратно в 'HashSet
Хорошо, но тогда нет никакой гарантии безопасности в любом отношении. Даже если _mine является частным членом экземпляра, вы можете получить его с помощью отражения. Если потребитель решил нарушить контракт, предоставленный API, то они должны принять последствия этого. Выставляя его как «IEnumerable
По общему соглашению, обычно. Существует большая разница между доступом к личным контактам и личным доступом. –
Вы также можете обеспечить последовательность так:
public IEnumerable<string> GetHashSetOneValues()
{
foreach (string value in hashSetOne)
yield return value;
}
Этот способ может быть вызван внутри цикла Еогеасп:
foreach (string value in myObject.GetHashSetOneValues())
DoSomething(value);
Вы можете также использовать метод Select
для создания оболочки, чем не может быть брошено назад HashSet<T>
:
public IEnumerable<int> Values
{
get { return _values.Select(value => value);
}
Th избегает повторного использования по сравнению с _values
дважды, как и с .ToArray()
, но сохраняет реализацию на одной чистой линии.
Великие мысли думают одинаково :) –
Хмм, интересно ... – strager
Предполагая, что вы используете .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
вызовет делегата проекции без операции для каждого полученного элемента.Шансы на то, что эта разница значительна, астрономически мала, однако - я предлагаю вам пойти с тем, что сделает код более ясным для вас.
+1. Я бы пошел с «Пропустить», чтобы избежать лишних лямбда над головой. Хотя это не очень важно, на самом деле это ничего не значит. –
+1; 'Skip (0)' отличная идея! – strager
Это может быть довольно поздно для вечеринки, но самым простым способом было бы использовать Linq. Вместо того, чтобы писать
public IEnumerable<string> GetValues()
{
foreach(var elem in list)
yield return elem;
}
вы можете написать
public IEnumerable<string> GetValues() => list;
Спасибо всем, особ. Страгер за самый полный ответ, и Джон Скит за очистку моего ОП. – DGH