Имеют ли свойства только для записи практическое применение? - PullRequest
48 голосов
/ 06 февраля 2010

Я не знаю, почему я начал думать об этом, но теперь я не могу остановиться.

В C # - и, возможно, во многих других языках, я помню, что Delphi разрешал вам делать это тоже - законно писать этот синтаксис:

class WeirdClass
{
    private void Hello(string name)
    {
        Console.WriteLine("Hello, {0}!", name);
    }

    public string Name
    {
        set { Hello(name); }
    }
}

Другими словами, свойство имеет сеттер , но не геттер , оно только для записи .

Полагаю, я не могу придумать причину, по которой это должно быть незаконно , но я никогда не видел его в дикой природе, и мне кажется, что какой-то довольно яркий / ужасающий код дикий. Это похоже на запах кода; похоже, что компилятор должен дать мне предупреждение:

CS83417: Свойство «Имя» выглядит совершенно бесполезным и глупым. Плохой программист! Попробуйте заменить методом.

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

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

Ответы [ 14 ]

16 голосов
/ 06 февраля 2010

Анализ кода (он же FxCop) дает вам диагностику:

CA1044 : Microsoft.Design: Потому что свойство WeirdClass.Name доступно только для записи, либо добавьте свойство getter с доступность, которая больше или равен его установщику или преобразовать это свойство в метод.

14 голосов
/ 06 февраля 2010

Моей первой реакцией на этот вопрос было: «А как насчет метода java.util.Random # setSeed

Я думаю, что свойства только для записи полезны в нескольких сценариях. Например, когда вы не хотите показывать внутреннее представление ( encapsulation ), в то же время позволяя изменить состояние объекта. Случайный пример - очень хороший пример такого дизайна.

12 голосов
/ 06 февраля 2010

Свойства только для записи на самом деле весьма полезны, и я часто их использую. Все дело в инкапсуляции - ограничении доступа к компонентам объекта. Вам часто нужно предоставить один или несколько компонентов классу, который он должен использовать для внутреннего использования, но нет причин делать их доступными для других классов. Это просто делает ваш класс более запутанным («Я использую этот метод получения или этот метод?»), И более вероятно, что ваш класс может быть подделан или обойдено его реальное назначение.

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

8 голосов
/ 25 февраля 2010

У меня есть код, подобный следующему в проекте XNA. Как видите, Scale предназначен только для записи, он полезен и (разумно) интуитивно понятен, а свойство чтения ( get ) для него не имеет смысла. Конечно, его можно заменить методом, но мне нравится синтаксис.

public class MyGraphicalObject
{
    public double ScaleX { get; set; }
    public double ScaleY { get; set; }
    public double ScaleZ { get; set; }

    public double Scale { set { ScaleX = ScaleY = ScaleZ = value; } }

    // more...
}
3 голосов
/ 24 августа 2013

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

Допустим, у меня был класс:

public class WhizbangService {
    public WhizbangProvider Provider { set; private get; }
}

WhizbangProvider не предназначен для доступа внешнего мира. Я бы никогда не хотел взаимодействовать с service.Provider, это слишком сложно. Мне нужен класс, как WhizbangService, чтобы действовать как фасад. Но с сеттером я могу сделать что-то вроде этого:

service.Provider = new FireworksShow();
service.Start();

И служба запускает фейерверк. Или, может быть, вы бы предпочли посмотреть водное и световое шоу:

service.Stop();
service.Provider = new FountainDisplay(new StringOfLights(), 20, UnitOfTime.Seconds);
service.Start();

И так далее ...

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

public abstract class DisplayService {
    public WhizbangProvider Provider { set; private get; }
}

public class WhizbangService : DisplayService  { }

Здесь альтернатива внедрению в конструктор:

public abstract class DisplayService {
    public WhizbangProvider Provider;

    protected DisplayService(WhizbangProvider provider) {
       Provider = provider ?? new DefaultProvider();
    }
}

public class WhizbangService : DisplayService  { 
    public WhizbangService(WhizbangProvider provider) 
      : base(provider) 
    { }
}

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

2 голосов
/ 17 марта 2018

Хотя .NET руководящие принципы проектирования рекомендуют использовать метод ("SetMyWriteOnlyParameter") вместо свойства только для записи, я считаю, что свойства только для записи полезны при создании связанных объектов из сериализованного представления (из базы данных).

Наше приложение представляет системы добычи нефти. У нас есть система в целом (объект «Модель») и различные объекты Reservoir, Well, Node, Group и т. Д.

Модель сначала создается и читается из базы данных - другие объекты должны знать, к какой модели они принадлежат. Тем не менее, Модель должна знать, какой нижний объект представляет общий объем продаж. Для этой информации имеет смысл хранить свойство Model. Если мы не хотим делать два чтения информации о модели, нам нужно иметь возможность прочитать имя объекта Sales перед его созданием. Затем, впоследствии, мы устанавливаем переменную «SalesObject», чтобы она указывала на реальный объект (чтобы, например, любое изменение пользователем имени этого объекта не вызывало проблем)

Мы предпочитаем использовать свойство только для записи - 'SalesObjectName = "TopNode"', а не метод - 'SetSalesObjectName ("TopNode") - потому что нам кажется, что последний предполагает существование SalesObject.

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

2 голосов
/ 14 сентября 2016

В шаблоне MVP обычно пишут свойство с установщиком в представлении (нет необходимости в получателе) - всякий раз, когда докладчик устанавливает его содержимое, свойство будет использовать это значение для обновления какого-либо элемента пользовательского интерфейса.

См. здесь для небольшой демонстрации:

public partial class ShowMeTheTime : Page, ICurrentTimeView
{
    protected void Page_Load(object sender, EventArgs e) 
    {
        CurrentTimePresenter presenter = new CurrentTimePresenter(this);
        presenter.InitView();
    }

    public DateTime CurrentTime 
    {
        set { lblCurrentTime.Text = value.ToString(); }
    }
}

Метод InitView презентатора просто устанавливает значение свойства:

public void InitView() 
{
    view.CurrentTime = DateTime.Now;
}
2 голосов
/ 30 июня 2012

Создание чего-либо только для записи полезно, когда вы не должны читать то, что пишете.

Например, при рисовании объектов на экране (это именно то, что делает Windows Window Manager на рабочем столе):
Вы, конечно, можете рисовать на экране, но вам никогда не придется считывать данные (не говоря уже о том, чтобы получить тот же дизайн, что и раньше).

Теперь, если полезны только для записи свойства (в отличие от методов), я не уверен, как часто они используются. Я полагаю, вы могли бы представить себе ситуацию со свойством «BackgroundColor», когда запись в него устанавливает цвет фона экрана, но чтение не имеет смысла (обязательно).
Так что я не уверен насчет этой части, но в целом я просто хотел указать, что есть сценариев использования для ситуаций, в которых вы только записываете данные и никогда не читаете их.

1 голос
/ 13 марта 2014

Я тоже не могу перестать думать об этом. У меня есть сценарий использования свойства «только для записи». Я не вижу хорошего выхода из этого.

Я хочу создать атрибут C #, производный от AuthorizeAttribute для приложения ASP.NET MVC. У меня есть служба (скажем, IStore), которая возвращает информацию, которая помогает решить, следует ли авторизовать текущего пользователя. Инъектор конструктора не сработает, потому что

public AllowedAttribute: AuthorizeAttribute
{
    public AllowedAttribute(IStore store) {...}
    private IStore Store { get; set; }
    ...
}

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

public AllowedAttribute: AuthorizeAttribute
{
    [Inject] public IStore Store { private get; set; }
    ...
}

Наряду со всеми другими плохими вещами в Property Setter вместо Constructor Injection, сервис доступен только для записи. Достаточно плохо, что мне приходится выставлять сеттер клиентам, которые не должны знать о деталях реализации. Никому бы не помешало, чтобы клиенты тоже видели геттера.

Я думаю, что преимущество Inpendency Injection превосходит рекомендации по свойствам только для записи для этого сценария, если только я что-то не упустил.

1 голос
/ 06 февраля 2010

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

Конечно, я пытаюсь доказать отрицание, так что, возможно, что-то мне не хватает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...