Дизайн на C# клас - излагане на променливи за четене, но не и настройка

Имам клас полигон, който съхранява списък на 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 израз премахва трите допълнителни стъпки, които imo са излишни.

Струва си да се спомене ефектът от .ToList(). Той ще създаде нов екземпляр на List<> и ще копира всички вектори в този списък. Страничен ефект от това е, че клиентите, съхраняващи препратката, ще имат списък, който на практика не е синхронизиран с оригинала. От друга страна, ако върнем LINQ израза директно, изброяването на върнатата препратка винаги ще изброи вътрешния списък.

person Peter Lillevold    schedule 01.05.2010

Още една вариация, използваща добив:

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