Есть ли причина иметь свойство без геттера?
Мой менеджер спросил меня, хорошо ли использовать свойство с установщиком, но без геттера.
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?
Ответы
Ответ 1
Официальная статья:
Рекомендации по разработке для разработки библиотек классов → Руководство по дизайну элементов → Дизайн недвижимости
Не предоставлять свойства только для установки.
Если свойство getter невозможно предоставить, используйте метод для реализации вместо этого. Имя метода должно начинаться с Set за которым следует имя свойства. Например, У AppDomain есть метод, называемый SetCachePath, вместо того, чтобы иметь свойство set-only, называемое CachePath.
Ответ 2
Принимая во внимание следующие вопросы: есть ли у кого-нибудь официальные статьи о свойствах без геттера? Или еще лучше - пример этого использования в самой .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, Version = 4.0.0.0, Culture = нейтральная, PublicKeyToken = b77a5c561934e089; Тип: FileIOAccess; Свойство: PathDiscovery
Сборка: mscorlib, Version = 4.0.0.0, Culture = нейтральная, PublicKeyToken = b77a5c561934e089; Тип: RedirectionProxy; Свойство: ObjectMode
Итак, похоже, что структура использует его экономно. Я предлагаю сделать то же самое.
ИЗМЕНИТЬ
Интересно, что запуск одного и того же кода с прикрепленным отладчиком дает гораздо больше результатов:
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
У меня нет официальных статей или примеров.. только мнение.
И, на мой взгляд, свойство, которое невозможно прочитать, - это зверь, который будет гневаться и запутаться.
Это сводится к намерениям. В собственности говорится: "Я намерен разрешить потребителю прочитать меня и, возможно, даже написать мне". Функция с именем "SetSomeAttribute" объявляет намерение только для записи.
Там также все, его мои данные, поэтому почему я не могу просто прочитать его обратно.
Таким образом, по моему мнению, никогда не было веской причины использовать свойство write-only.
Ответ 4
Нет ничего плохого в том, что у вас есть свойство без геттера, если это делает ваш код наиболее понятным и поддерживаемым. Однако хорошие случаи для этого, вероятно, крайне редки.
Лучшая вещь, которую я когда-либо использовал без потерь, - это классы модульных тестов, свойства которых имеют недоступные сеттеры. Например:
public class MyClass
{
public int MyId { get; protected set; }
}
public class MyClass_Test : MyClass
{
public int MyId_Set
{
set { MyId = value; }
}
}
Таким образом, я могу использовать MyClass_Test
в unit test и предварительно установить значение MyId
с возможностью unit test определенного метода.
Кроме того, в ответ на ваш пример использование 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; }
}
}
Ответ 5
Нечего сказать, что вы не можете этого сделать, но это кажется немного странным. Я бы получил частную собственность и метод, который установил бы его.
private string _test;
public void SetTest(string test)
{
_test = test;
}
Ответ 6
Вы должны помнить, почему существуют свойства. Они заменяют следующий шаблон
class Foo
{
private Bar _bar;
public Bar GetBar()
{
return _bar;
}
public void SetBar(Bar bar)
{
_bar = bar;
}
}
Хотя свойство с сеттером выглядит странно для меня, я не думаю, что наличие метода Set без метода Get вообще выглядит нечетным.
На самом деле, я уверен, что свойства являются синтаксическим сахаром и переписываются как методы get/set. Или, по крайней мере, очень близко.
Ответ 7
Они потенциально могут использоваться для инъекций зависимостей, когда по какой-либо причине ни конструктор, ни метод инъекций не работают для вас.
Но я не могу представить такой случай.
Ответ 8
Ну, они могут быть полезны для частных полей в инициализаторах объектов:
var obj = new MyClass
{
Prop1 = value1,
Prop2 = value2
};
который может быть альтернативным способом просто написать их как параметры конструктора:
var obj = new MyClass(value1, value2);
Но если есть много (необязательных) параметров, могут быть удобны свойства writeonly.
Ответ 9
Как уже было предложено, я думаю, что ответ - это то, что наиболее значимо в контексте проблемы. Я могу подумать о двух альтернативах наличию свойства setter, которое может стоить попробовать, чтобы увидеть, что наиболее значимо:
- Определите отдельный метод, который принимает единственный параметр.
- Определите получателя, который всегда возвращает одно и то же значение (например, null). Это зависит от контекста.
Ответ 10
У вас могут быть свойства, которые являются более аккуратными и над которыми вы можете осуществлять полный контроль доступа, без беспорядочного _localVariable и синтаксиса свойств.
Примеры :
/// <summary>
/// Publicly readable, privately settable.
/// </summary>
public int FirstProperty { get; private set; }
/// <summary>
/// Entirely private
/// </summary>
private int SecondProperty { get; set; }