Дизайн класса С# - выставляйте переменные для чтения, но не для установки

У меня есть класс многоугольника, в котором хранится список Microsoft.Xna.Framework.Vector2 в качестве вершин многоугольника. После создания многоугольника я бы хотел, чтобы другие классы могли считывать положение вершин, но не изменять их.

В настоящее время я выставляю вершины через это поле:

/// <summary>
/// Gets the vertices stored for this polygon.
/// </summary>
public List<Vector2> Vertices
{
    get { return _vertices; }
}
List<Vector2> _vertices;

Однако вы можете изменить любую вершину, используя такой код:

Polygon1.Vertices[0] = new Vector2(0, 0);

or

Polygon1.Vertices[0].X = 0;

Как я могу ограничить другие классы, чтобы иметь возможность только читать свойства этих вершин и не иметь возможности устанавливать новый в свой список? Единственное, о чем я могу думать, это передать копию классам, которые ее запрашивают.

Обратите внимание, что Vector2 — это структура, являющаяся частью среды XNA, и я не могу ее изменить.

Спасибо.


person Community    schedule 01.05.2010    source источник


Ответы (4)


Имейте интерфейс только для чтения для вашего типа (однако, поскольку он встроен в тип) и раздайте версию в ReadOnlyCollection предназначен для.

public ReadOnlyCollection<IReadOnlyVector2> Vertices
{
    get { return (from v in _vertices 
                  select new DerivedVector2 { WrappedVector2 = v })
                 .Cast<IReadOnlyVector2>().ToList().AsReadOnly(); 
     }
}
List<Vector2> _vertices;

interface IReadOnlyVector2 {
 .. only RO getters and no setters
}

class DerivedVector2 : IReadOnlyVector2{
    public Vector2 WrappedVector2 { get; internal set;} 
.
.
.

}

person Preet Sangha    schedule 01.05.2010
comment
вызов _vertices.AsReadOnly() обычно предпочтительнее. - person Adam Robinson; 01.05.2010
comment
Не знал об этом! Спасибо - person Preet Sangha; 01.05.2010
comment
Если вы вызываете Cast(), вы могли бы просто открыть IEnumerable<IReadOnlyVector2>, предполагая, что этот интерфейс обладает необходимой вам функциональностью. Функция Cast эффективно изолирует исходный список от потребляющего кода (как и любая другая преобразующая функция в Enumerable). - person Adam Robinson; 01.05.2010

Делайте то, что часто делает XNA Framework:

private List<Vector2> myVectors = new List<Vector2>();

public Vector2[] Vertices { get { return myVectors.ToArray(); } }
person Krisc    schedule 01.05.2010

Просто хотел предложить вариант Preets ответ.

public IEnumerable<IReadOnlyVector2> Vertices
{
    get
    {
        return from v in _vertices 
               select (IReadOnlyVector2)new DerivedVector2(v);
    }
}

Возврат IEnumerable<> по своей сути доступен только для чтения, поскольку клиенты могут только перечислять, а не изменять. Кроме того, упрощенное выражение LINQ удаляет три дополнительных шага, которые, по моему мнению, являются излишними.

Стоит упомянуть эффект .ToList(). Он создаст новый экземпляр List<> и скопирует все векторы в этот список. Побочным эффектом этого является то, что клиенты, хранящие ссылку, будут иметь список, который фактически не синхронизирован с оригиналом. С другой стороны, если мы вернем выражение LINQ напрямую, перечисление возвращаемой ссылки всегда будет перечислять внутренний список.

person Peter Lillevold    schedule 01.05.2010

Еще один вариант с использованием yield:

List<Vector2> _vertices;
public IEnumerable<Vector2> Vertices
{
    get
    {
        foreach (Vector2 vec in _vertices)
            yield return vec;
    }
}

Хотя я бы, наверное, сказал, что ответ Криска лучший...

Рядом с _vertices.AsReadOnly(), конечно: D

person YellPika    schedule 26.05.2010