2009-07-20 3 views
1

Я оглядывался и до сих пор не смог найти хороший способ сделать это. Я уверен, это обычная проблема.Есть ли в C#/CLR механизм для маркировки возвращаемых значений свойств как для чтения/неизменяемости?

Предположим, у меня есть следующие:

class SomeClass : IComparable 
{ 
    private int myVal; 
    public int MyVal 
    { 
     get { return myVal; } 
     set { myVal = value; } 
    } 

    public int CompareTo(object other) { /* implementation here */ } 
} 

class SortedCollection<T> 
{ 
    private T[] data; 
    public T Top { get { return data[0]; } } 

    /* rest of implementation here */ 
} 

Идея заключалась в том, что я собираюсь реализовать бинарное кучу, и вместо того, поддерживают только Insert() и DeleteMin() операции, я хочу поддержать " peeking "при наивысшем (или самом низком, в зависимости от обстоятельств) значении приоритета в стеке. Никогда не нравился Гейзенберг, и все это «вы не можете смотреть на вещи, не меняя их». Принцип неопределенности. Мусор!

Проблема заключается в том, что вышеизложенное не позволяет предотвратить изменение кода вызова MyVal (при условии SortedCollection) через свойство Top, операция которого имеет четкую возможность поместить мою кучу в неправильном порядке. Есть ли способ предотвратить внесение изменений в внутренние элементы кучи через свойство Top? Или я просто использую код с предупреждением: «Только стабильный, если вы не изменяете какие-либо экземпляры между временем их вставки и dequeue'd. YMMV».

+0

Возможно, вы захотите реализовать неизменяемую очередь приоритетов. Каждая очередь неизменна; добавление или удаление элемента возвращает совершенно новую очередь. У меня есть исходный код C# для такой вещи где-то; Я посмотрю, смогу ли я его выкопать. –

+0

Hrm ... Мне нравится идея, но я не думаю, что она в конечном итоге решает проблему, потому что проблема, которую я ищу, касается недействительности заказа из-за модификации существующего элемента, а не вставки/удаления. – Dathan

ответ

1

Чтобы ответить на ваш вопрос: Нет, есть нет способа реализовать такое поведение, которое вы хотите - до тех пор, пока T имеет ссылочный тип (и, возможно, даже с некоторыми типами значений)

Вы не можете действительно много чего поделать. Пока вы предоставляете getter, вызывающий код может изменять внутреннее содержимое ваших данных в зависимости от доступности данных (т. Е. Свойств, полей и методов).

class SomeClass : IComparable 
{ 
    private int myVal; 
    public int MyVal 
    { 
     get { return myVal; } 
     set { myVal = value; } 
    } 

    public int CompareTo(object other) { /* implementation here */ } 
} 


class SortedCollection<T> 
{ 
    private T[] data; 
    public T Top { get { return data[0]; } } 

    /* rest of implementation here */ 
} 

//.. 
// calling code 
SortedCollection<SomeClass> col; 
col.Top.MyVal = 500; // you can't really prevent this 

Примечание Что я имею в виду, вы не можете предотвратить это в случае классов, которые вы не контролируете.В этом примере, как и другие, вы можете сделать набор MyVal частным или опустить его; но так как SortedColleciton это общий класс, вы не можете ничего о других людях структурах делать ..

+0

Вы попадаете в гвоздь прямо на голову - спасибо. То, что я рассматриваю в этом случае, на самом деле идет немного дальше и использует что-то вроде Castle DynamicProxy для создания обертки вокруг отсортированного типа и возвращает эту оболочку как элемент Top. Он представляет некоторые проблемы, но может работать в некоторых случаях. Я, вероятно, также буду реализовывать поддержку для обнаружения любых событий INotifyPropertyChanged, если тип поддерживает их и при необходимости использует кучу, и поддерживает решение ICloneable, описанное в другом месте. Это должно охватывать множество случаев. А если нет, по крайней мере, я попробовал. – Dathan

1

Вы можете иметь свойство только для чтения (то есть свойство только геттер):

private int myVal; 
public int MyVal { get { return myVal; } } 

Но будьте осторожны: это не всегда может работать так, как вы ожидаете. Рассмотрим:

private List<int> myVals; 
public List<int> MyVals { get { return myVals; } } 

В этом случае, вы не можете изменить список, в который класс использует, но вы все еще можете назвать .Add(), .Remove(), и т.д. методы этого списка.

+0

В этом случае я могу вернуть ReadOnlyCollection в зависимости от ситуации. +1 –

+0

Хотя это несколько не относится к этой теме/ответу. –

0

только реализовать добытчик для свойств и модифицировать коллекцию, имея добавить/удалить методы

1

Ваших свойства не должны иметь такой же доступ к get/set. Это покрывает вас за все, что возвращает значение типа (обычно struct с, которое содержит только типы значений) или неизменяемые ссылочные типы.

public int MyVal 
{ 
    get { return myVal; } 
    private set { myVal = value; } 
} 

Для изменяемых ссылочных типов, у вас есть другие варианты, такие как возвращение Clone() с или с помощью ReadOnlyCollection<T>, чтобы вызывающие их изменения:

private List<int> data; 

public IList<int> Data 
{ 
    get { return new ReadOnlyCollection<int>(this.data); } 
} 
+0

FYI - Компилятор C# поддерживает немного синтаксического сахара для автоматических свойств только для чтения: public int MyVal {get; частный набор; }. Это может сделать ваши объявления свойств немного более чистыми, если у вас есть несколько из этих свойств на одном типе. – Levi

+0

Мне нравится идея клонирования ... Я не хочу ограничивать коллекцию поддержкой только типов клонируемых данных, но в случае, когда тип IS cloneable, условно поддерживающий такое поведение, может помочь смягчить мои страхи. Благодаря! – Dathan

+0

@Levi Спасибо за информацию. Я включил этот код в проект VS 2005, хотя ... Мое понимание заключается в том, что авто-свойства поддерживаются начиная с 2008 года? – Dathan

0

Я понимаю вашу проблему сейчас. Я думаю, что это должно сработать:

class SortedCollection<T> where T: ICloneable 
{ 
    private T[] data; 
    public T Top 
    { 
     get 
     { 
      T ret = (T)data[0].Clone(); 
      return ret; 
     } 
    } 

    /* rest of implementation here */ 
} 

Ограничение ICloneable гарантирует, что параметр типа реализует интерфейс ICloneable. (если это приемлемо)