Как я должен использовать свойства при контакте со Списком только для чтения <T> участников

Почему перечисления обработки по-другому к другим типам? Сохраните их в том же пространстве имен, поскольку они, вероятно, будут использоваться - и предположение, что они собираются быть используемыми другими классами, делают их типами верхнего уровня в их собственных файлах.

единственный тип типа, который я делаю обычно глыба вместе, является делегатами - у меня иногда есть файл Delegates.cs с набором делегатов в. Меньше с.NET 3.5 и Func/Action, заметьте.

10
задан goodeye 9 August 2012 в 00:09
поделиться

7 ответов

Вы можете выставить его AsReadOnly . То есть вернуть доступную только для чтения оболочку IList . Например ...

public ReadOnlyCollection<int> List
{
    get { return _lst.AsReadOnly(); }
}

Простого возврата IEnumerable недостаточно. Например ...

void Main()
{
    var el = new ExposeList();
    var lst = el.ListEnumerator;
    var oops = (IList<int>)lst;
    oops.Add( 4 );  // mutates list

    var rol = el.ReadOnly;
    var oops2 = (IList<int>)rol;

    oops2.Add( 5 );  // raises exception
}

class ExposeList
{
  private List<int> _lst = new List<int>() { 1, 2, 3 };

  public IEnumerable<int> ListEnumerator
  {
     get { return _lst; }
  }

  public ReadOnlyCollection<int> ReadOnly
  {
     get { return _lst.AsReadOnly(); }
  }
}

В ответе Стива также есть хитрый способ избежать каста.

18
ответ дан 3 December 2019 в 14:25
поделиться

Ответ JP относительно возврата IEnumerable верен (вы можете преобразовать его в список), но вот метод, который предотвращает приведение вниз.

class ExposeList
{
  private List<int> _lst = new List<int>() { 1, 2, 3 };

  public IEnumerable<int> ListEnumerator
  {
     get { return _lst.Select(x => x); }  // Identity transformation.
  }

  public ReadOnlyCollection<int> ReadOnly
  {
     get { return _lst.AsReadOnly(); }
  }
}

Преобразование идентичности во время перечисления эффективно создает генерируемый компилятором итератор - новый тип, который никак не связан с _lst .

5
ответ дан 3 December 2019 в 14:25
поделиться

Есть ограниченная ценность попыток скрыть информацию до такой степени. Тип свойства должен указывать пользователям, что им разрешено с ним делать. Если пользователь решит злоупотребить вашим API, он найдет способ. Блокирование их от кастинга не останавливает их:

public static class Circumventions
{
    public static IList<T> AsWritable<T>(this IEnumerable<T> source)
    {
        return source.GetType()
            .GetFields(BindingFlags.Public |
                       BindingFlags.NonPublic | 
                       BindingFlags.Instance)
            .Select(f => f.GetValue(source))
            .OfType<IList<T>>()
            .First();
    }
}

С помощью этого единственного метода мы можем пока обойти три ответа, данные на этот вопрос:

List<int> a = new List<int> {1, 2, 3, 4, 5};

IList<int> b = a.AsReadOnly(); // block modification...

IList<int> c = b.AsWritable(); // ... but unblock it again

c.Add(6);
Debug.Assert(a.Count == 6); // we've modified the original

IEnumerable<int> d = a.Select(x => x); // okay, try this...

IList<int> e = d.AsWritable(); // no, can still get round it

e.Add(7);
Debug.Assert(a.Count == 7); // modified original again

Также:

public static class AlexeyR
{
    public static IEnumerable<T> AsReallyReadOnly<T>(this IEnumerable<T> source)
    {
        foreach (T t in source) yield return t;
    }
}

IEnumerable<int> f = a.AsReallyReadOnly(); // really?

IList<int> g = f.AsWritable(); // apparently not!
g.Add(8);
Debug.Assert(a.Count == 8); // modified original again

Повторяю ... это своего рода "гонка вооружений" "может продолжаться сколько угодно!

Единственный способ остановить это - полностью разорвать связь с исходным списком, что означает, что вам нужно сделать полную копию исходного списка. Это то, что делает BCL, когда возвращает массивы. Обратной стороной этого является то, что вы возлагаете потенциально большие затраты на 99,9% ваших пользователей каждый раз, когда они хотят получить доступ только для чтения к некоторым данным, потому что вы беспокоитесь о хакерских действиях 00,1% пользователей.

Или вы можете просто отказаться для поддержки использования вашего API в обход системы статических типов.

Если вы хотите, чтобы свойство возвращало список только для чтения с произвольным доступом, верните что-то, что реализует:

public interface IReadOnlyList<T> : IEnumerable<T>
{
    int Count { get; }
    T this[int index] { get; }
}

Если (что гораздо чаще) его нужно только перечислять последовательно, просто верните IEnumerable :

public class MyClassList
{
    private List<int> li = new List<int> { 1, 2, 3 };

    public IEnumerable<int> MyList
    {
        get { return li; }
    }
}

ОБНОВЛЕНИЕ Поскольку я написал этот ответ, вышел C # 4.0,

10
ответ дан 3 December 2019 в 14:25
поделиться

Эрик Липперт опубликовал в своем блоге серию статей о неизменяемости в C #.

Первую статью из этой серии можно найти здесь .

Вы также можете найти полезный ответ Джона Скита на аналогичный вопрос .

1
ответ дан 3 December 2019 в 14:25
поделиться
public static IEnumerable<T> AsReallyReadOnly<T>(this IEnumerable<T> source)
{
    foreach (T t in source) yield return t;
}

, если я добавлю к примеру Earwicker

...
IEnumerable<int> f = a.AsReallyReadOnly();
IList<int> g = f.AsWritable(); // finally can't get around it

g.Add(8);
Debug.Assert(a.Count == 78);

, я получу InvalidOperationException: Sequence не содержит соответствующего элемента .

0
ответ дан 3 December 2019 в 14:25
поделиться
public List<int> li;

Не объявляйте общедоступные поля, это обычно считается плохой практикой ... вместо этого оберните его в свойство.

Вы можете предоставить свою коллекцию как ReadOnlyCollection:

private List<int> li;
public ReadOnlyCollection<int> List
{
    get { return li.AsReadOnly(); }
}
0
ответ дан 3 December 2019 в 14:25
поделиться
public class MyClassList
{
    private List<int> _lst = new List<int>() { 1, 2, 3 };

    public IEnumerable<int> ListEnumerator
    {
        get { return _lst.AsReadOnly(); }
    }

}

Чтобы проверить

    MyClassList  myClassList = new MyClassList();
    var lst= (IList<int>)myClassList.ListEnumerator  ;
    lst.Add(4); //At this point ypu will get exception Collection is read-only.
0
ответ дан 3 December 2019 в 14:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: