Подтвердить что ты не робот

Имеются ли свойства для записи только в практических приложениях?

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

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

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

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

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

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

CS83417: Имущество 'Name' выглядит совершенно бесполезным и глупым. Плохой программист! Рассмотрите возможность замены методом.

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

Есть ли реальные примеры свойств для записи, которые либо не могут быть заменены прямыми вызовами метода, либо станут менее интуитивными?

4b9b3361

Ответ 1

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

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

Ответ 2

Моя первая реакция на этот вопрос: "Как насчет метода java.util.Random # setSeed?

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

Ответ 3

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

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

Ответ 4

У меня есть код, похожий на следующий в проекте XNA. Как вы можете видеть, Масштаб предназначен только для записи, он полезен и (разумно) интуитивно понятен, и свойство read (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...
}

Ответ 5

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

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

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

Ответ 6

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

Скажем, у меня был класс:

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, вы получите разумное значение по умолчанию.

Ответ 7

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

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

Ответ 8

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

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

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

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

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

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

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

Ответ 9

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

Гипотетический случай:

CommunicationDevice.Response = "Hello, World" 

вместо

CommunicationDevice.SendResponse("Hello, World")

Основная задача - выполнять побочные эффекты IO или проверку.

Интересно, что VB.NET даже получил свое собственное ключевое слово для этого странного вида свойства;)

Public WriteOnly Property Foo() As Integer
   Set(value As Integer)
     ' ... '
   End Set
End Property

хотя многие свойства "только для записи" извне имеют частный получатель.

Ответ 10

Недавно я работал над приложением, которое обрабатывало пароли. (Обратите внимание, что я не утверждаю, что следующее - хорошая идея, я просто описываю, что я сделал.)

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

Итак, у меня был другой объект, я не помню, что это было; пусть делают вид, что это защищенный паролем банан. Класс Banana имел свойство set-only, называемое Password, которое создало HashingPassword из данного значения и сохраняло его в частном атрибуте Banana. Поскольку атрибут Password HashingPassword был закрытым, не было способа написать getter для этого свойства.

Итак, почему у меня есть свойство set-only, называемое Password вместо метода с именем SetPassword? Потому что это имело смысл. Фактически, эффект был задан для пароля Banana, и если бы я хотел установить пароль объекта Banana, я бы ожидал этого, установив свойство, а не вызывая метод.

Использование метода под названием SetPassword не имело бы каких-либо серьезных недостатков. Но я не вижу никаких существенных преимуществ.

Ответ 11

Я знаю, что это было здесь уже давно, но я наткнулся на него и допустил (imho) прецедент:

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

public int MyFancyWepapiMethod([FromBody]CallParams p) {
    return p.MyIntPropertyForAjax.HasValue ? p.MyIntPropertyForAjax.Value : 42;
}

public class CallParams
{
    public int? MyIntPropertyForAjax;

    public object TryMyIntPropertyForAjax
    {
        set
        {
            try { MyIntPropertyForAjax = Convert.ToInt32(value); }
            catch { MyIntPropertyForAjax = null; }
        }
    }
}

На стороне JavaScript вы можете просто заполнить параметры, включая проверку:

var callparameter = {
    TryMyIntPropertyForAjax = 23
}

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

Ответ 12

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

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

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;
    }

Ответ 13

Ну, технически нет ничего плохого в свойстве Setter-per-se. Сеттер может выполнить некоторую проверку, и, возможно, некоторые другие методы - публичные или частные - могут зависеть от нее.

Например:

class SomeClass {
    private int _someVar;

    SomeClass(int someVar) {
        if(someVar > 0) _someVar = someVar;
    }

    public int SomeVar {
        set { if(value > 0) _someVar = value; }
    }

     public string SomeFunction(){
       return "This is an important function that uses _someVar " + _someVar;
     }
}

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