Есть ли причина иметь собственность без добытчика? - PullRequest
15 голосов
/ 02 марта 2012

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

public class PropertyWrapper
{   
    private MyClass _field;

    public MyClass Property
    {
        set { _field = value; } 
    }

    public string FirstProperty
    {
        get { return _field.FirstProperty; } 
    }

    public string SecondProperty
    {
        get { return _field.SecondProperty; } 
    }
}

Он будет использовать другие свойства для предоставления свойств из частного поля, установленного этим установщиком.

Мое предложение состояло в том, чтобы просто использовать приватное поле и установить его в конструкторе, который прекрасно работает в этом сценарии. Если бы мне сначала нужно было создать построенный объект (возможно, даже с использованием полиморфизма), я бы все же предпочел метод Load, а не свойство без получателя.

Но мне интересно. Мы оба очень обеспокоены передовой практикой и стараемся убедиться, что наш код стандартизирован. У кого-нибудь есть официальные статьи о свойствах без геттеров? Или еще лучше - пример такого использования в самом .NET Framework?

Ответы [ 10 ]

17 голосов
/ 02 марта 2012

Официальная статья: Руководство по разработке библиотек классов -> Правила разработки элементов -> Дизайн недвижимости

Не предоставлять свойства только для набора.

Если средство получения свойства не может быть предоставлено, используйте метод для реализации функциональность вместо. Имя метода должно начинаться с Set сопровождаемый тем, что было бы названием свойства. Например, В AppDomain есть метод SetCachePath вместо свойство только для набора с именем CachePath.

9 голосов
/ 02 марта 2012

Учитывая вопросы: Есть ли у кого-нибудь официальные статьи о свойствах без геттера? Или еще лучше - пример такого использования в самом .NET Framework? , а не о мнении; Я написал быстрое тестовое приложение, чтобы прочитать все свойства всех типов всех сборок, загруженных в консольное приложение по умолчанию:

foreach (var assem in AppDomain.CurrentDomain.GetAssemblies())
{
    foreach (var type in assem.GetTypes())
    {
        foreach (var prop in type.GetProperties())
        {
            if (!prop.CanRead)
                Console.WriteLine("Assembly: {0}; Type: {1}; Property: {2}", assem.FullName, type.Name, prop.Name);
        }
    }
}

Результаты:

Сборка: mscorlib, версия = 4.0.0.0, культура = нейтральная, PublicKeyToken = b77a5c561934e089; Тип: FileIOAccess; Недвижимость: PathDiscovery

Сборка: mscorlib, версия = 4.0.0.0, культура = нейтральная, PublicKeyToken = b77a5c561934e089; Тип: RedirectionProxy; Свойство: ObjectMode

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

EDIT

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

Assembly: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: FileIOAccess; Property: PathDiscovery
Assembly: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: RedirectionProxy; Property: ObjectMode
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: AxHost; Property: Site
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DataGridTextBoxColumn; Property: PropertyDescriptor
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DisplayedBandsData; Property: FirstDisplayedFrozenCol
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DisplayedBandsData; Property: FirstDisplayedFrozenRow
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DisplayedBandsData; Property: LastDisplayedFrozenCol
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DisplayedBandsData; Property: LastDisplayedFrozenRow
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DisplayedBandsData; Property: LastDisplayedScrollingRow
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ErrorProvider; Property: Site
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: WebBrowserBase; Property: Site
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: WebBrowser; Property: Site
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DropDownButton; Property: UseComboBoxTheme
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: GridErrorDlg; Property: Details
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: GridErrorDlg; Property: Message
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DropDownHolder; Property: ResizeUp
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: GridViewEdit; Property: DontFocus
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: GridViewEdit; Property: DisableMouseHook
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: MouseHook; Property: DisableMouseHook
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ManifestSignedXml; Property: Resolver
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ContainerProxy; Property: Bounds
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: RightToLeftProxy; Property: Bounds
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: TopDownProxy; Property: Bounds
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: BottomUpProxy; Property: Bounds
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ElementProxy; Property: Bounds
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: VerticalElementProxy; Property: Bounds
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: IconComparer; Property: SortOrder
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: MultiPropertyDescriptorGridEntry; Property: PropertyValue
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ConfigXmlDocument; Property: XmlResolver
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ConfigXmlDocument; Property: InnerText
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ProcessThread; Property: IdealProcessor
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ProcessThread; Property: ProcessorAffinity
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ConfigXmlAttribute; Property: InnerText
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ConfigXmlAttribute; Property: InnerXml
Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: AnonymousPipeServerStream; Property: ReadMode
Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: AnonymousPipeClientStream; Property: ReadMode
Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: ManifestSignedXml; Property: Resolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlResolver; Property: Credentials
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlNullResolver; Property: Credentials
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlSecureResolver; Property: Credentials
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlUrlResolver; Property: Credentials
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlUrlResolver; Property: Proxy
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlUrlResolver; Property: CachePolicy
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlReaderSettings; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlTextReader; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlValidatingReader; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DocumentXmlWriter; Property: NamespaceManager
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DocumentXmlWriter; Property: Navigator
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: DocumentXmlWriter; Property: EndNode
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlAttribute; Property: InnerText
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlAttribute; Property: InnerXml
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlDocument; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlDocument; Property: InnerText
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlUnspecifiedAttribute; Property: InnerText
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlUnspecifiedAttribute; Property: InnerXml
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlPreloadedResolver; Property: Credentials
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XslTransform; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlSchemaSet; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XmlSchemaValidator; Property: XmlResolver
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089; Type: XsdValidator; Property: Context
3 голосов
/ 02 марта 2012

У меня нет официальных статей или примеров ... только мнение.

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

Он приходитдо намерения.В собственности говорится: «Я намерен позволить потребителю читать меня и, возможно, даже писать мне».Функция с именем что-то вроде «SetSomeAttribute» объявляет намерение только для записи.

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

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

2 голосов
/ 02 марта 2012

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

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

public class MyClass
{
    public int MyId { get; protected set; }
}

public class MyClass_Test : MyClass
{
    public int MyId_Set
    {
        set { MyId = value; }
    }
}

Таким образом, я могу использовать MyClass_Test в модульном тесте и предварительно установить значение MyId с возможностью модульного тестирования определенного метода.

Кроме того, в прямом ответе на ваш пример лучше использовать private get:

public class PropertyWrapper
{       
    public MyClass Property { private get; set; }

    public string FirstProperty
    {
        get { return Property.FirstProperty; } 
    }

    public string SecondProperty
    {
        get { return Property.SecondProperty; } 
    }
}
0 голосов
/ 02 марта 2012

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

  1. Определить отдельный метод, который принимает один параметр.* Определить метод получения, который всегда возвращает одно и то же значение (например, ноль).Имеет ли это смысл или сюрпризы, зависит от контекста.
0 голосов
/ 02 марта 2012

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

var obj = new MyClass
{
    Prop1 = value1,
    Prop2 = value2
};

, который может быть альтернативным способом просто записать их в качестве параметров конструктора:

var obj = new MyClass(value1, value2);

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

0 голосов
/ 02 марта 2012

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

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

0 голосов
/ 02 марта 2012

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

примеры:

    /// <summary>
    /// Publicly readable, privately settable.
    /// </summary>
    public int FirstProperty { get; private set; }

    /// <summary>
    /// Entirely private
    /// </summary>
    private int SecondProperty { get; set; }
0 голосов
/ 02 марта 2012

Вы должны помнить, почему существуют свойства. Они заменяют следующий шаблон

class Foo
{
     private Bar _bar;

     public Bar GetBar() 
     { 
         return _bar; 
     }

     public void SetBar(Bar bar) 
     { 
         _bar = bar; 
     }
}

Несмотря на то, что свойство без Setter выглядит странно, я не думаю, что метод Set без метода Get выглядит странно.

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

0 голосов
/ 02 марта 2012

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

    private string _test;

public void SetTest(string test)
{
 _test = test;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...