В коде API, это хорошая идея использовать частный установщик для свойств типа C # списка? - PullRequest
2 голосов
/ 29 июля 2011

Я ищу мнение о чем-то.Рассмотрим следующий код из класса SomeApiObject:

// Property with private setter
public IList<string> SomeList
{
    get;
    private set;
}

// Constructor
public SomeApiObject()
{
    SomeList = new List<string>();
}

При такой настройке пользователи класса SomeApiObject не могут переназначить свойство SomeList, но то, что они могут do - манипулировать существующим списком, используя такие методы, как Add(), Remove() и Clear().

. Преимущество этого шаблона в том, что свойство гарантированно никогда не будет null, чтоЭто может быть очень удобным предположением, поскольку пользователь работает с API, поскольку это означает, что пользователь всегда может перебирать список, получать размер списка или добавлять к нему, даже не проверяя null.

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

Я с этим воздерживаюсь и ищу мнения.

  • Являются ли потенциальные недостатки слишком неприятными для пользователя API?
  • Является ли гарантия того, что функция null никогда не будет настолько приятной, как мне кажется?

РЕДАКТИРОВАТЬ:

Я уже убежден в преимуществахиспользования какого-то шаблона «никогда не ноль».Что меня больше интересует, так это чтобы кто-то сыграл адвоката дьявола и показал мне, почему наличие частного сеттера в списке, которым нужно манипулировать, может раздражать и / или запрещать с точки зрения пользователя API.

Некоторое время назад я выпустил оболочку .NET API, и до сих пор несколько пользователей не понимали, как присваивать значения таким свойствам, как этот.

Ответы [ 5 ]

5 голосов
/ 29 июля 2011

Никогда ноль - хорошая конструктивная особенность. Что касается раскрытия только списков в качестве свойств, доступных только для чтения, то это предложено в качестве рекомендации в моей любимой книге рекомендаций: http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613

Хотя лучший подход состоит в том, чтобы не позволять внешним вызывающим программам напрямую манипулировать состоянием вашего объекта и делать класс неизменным:

public class MyClass
{
   private List<string> _inner = new List<string>();

   public IEnumerable<string> Items
   {
      get { return _inner.GetEnumerator(); }
   }

   public void AddItem(string item);
   {
      _inner.Add(item);
   }
}
2 голосов
/ 29 июля 2011

Еще один момент, который следует учитывать, заключается в том, что вопрос зависит от того, «принадлежит» ли объект списку (состав) или «имеет» список (агрегация). Если объекту принадлежит список, сеттер должен быть закрытым; если он просто имеет список из другого объекта, сеттер может быть общедоступным.

Сложность в случае компоновки (кроме возможности присвоения списку нулевого значения) состоит в том, что открытый установщик заставляет класс уступить весь контроль над реализацией списка. Предположим, например, у вас есть реализация:

public IList<string> SomeList
{
    get;
    private set;
}
public SomeApiObject()
{
    SomeList = new List<string>();
}

Теперь предположим, что в следующей версии вы хотите использовать SomeSpecializedList, который реализует IList<string> вместо List<String>. Вы можете легко рефакторинг:

private SomeSpecializedList specializedList;
public IList<string> SomeList
{
    get {return specializedList;}
    private set {specializedList = value as SomeSpecializedList;}
}

public SomeApiObject()
{
    SomeList = new SomeSpecializedList<string>();
}

Изменилась только частная реализация; пользователи класса не затронуты. Но если бы сеттер был общедоступным, клиент мог бы потенциально передать любой экземпляр IList в SomeList, и это было бы серьезным изменением.

2 голосов
/ 29 июля 2011

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

public IEnumerable<string> SomeList
{
   get { return list.GetEnumerator(); }
   private set {}
}

Это позволит пользователю по-прежнему использовать коллекцию (также будет поддерживать linq) и защитит коллекцию от внешних манипуляций.

0 голосов
/ 29 июля 2011

Как бы мне не хотелось это говорить, это действительно зависит от того, что вы пытаетесь сделать с помощью своего API.Если вы хотите предоставить пользователю коллекцию, с которой он может работать, то ответ @ Matthew уместен.Если вы хотите скрыть коллекцию или разрешить только определенные действия (например, Add, Remove и т. Д.), Вы можете скрыть коллекцию и открыть для нее фасад:

private IList<string> _someList = new List<string>();

public void Add(string item){ _someList.Add(item); }
public string Remove(string item) { return _someList.Remove(item); }
...

Надеюсь, это поможет.

0 голосов
/ 29 июля 2011

это зависит от ситуации. в некоторых случаях может иметь смысл иметь нулевой список, в других - не так много. Вы должны спросить себя, имеет ли это смысл. Сам .net делает это в обоих направлениях.

...