有没有理由拥有一个没有getter的属性

本文关键字:属性 getter 拥有 有理由 有一个 | 更新日期: 2023-09-27 18:26:23

我的经理问我,使用带setter但不带getter的属性是否是一种好的做法。

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

他将使用其他属性来公开这个setter设置的私有字段中的属性。

我的建议是只使用一个私有字段并在构造函数中设置它,这在这种情况下很好。如果我需要先构建一个对象(甚至可能使用多态性),我仍然更喜欢Load方法,而不是无getter属性。

但我很感兴趣。我们都非常关注最佳实践,并努力确保我们的代码标准化。有人有关于getter-less属性的官方文章吗?或者更好的是,在.NET Framework中使用这种用法的一个例子?

有没有理由拥有一个没有getter的属性

官方文章:开发类库的设计指南->会员设计指南->房地产设计

不提供仅限集的属性

如果不能提供getter属性,请使用方法来实现取而代之的是功能。方法名称应以Set开头然后是属性名称。例如AppDomain有一个名为SetCachePath的方法,而不是仅设置名为CachePath的属性。

考虑到的问题是:有人有关于无getter属性的官方文章吗?或者更好的是,在.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

因此,该框架似乎很少使用它。我建议你也这样做。

编辑

有趣的是,在附加调试器的情况下运行相同的代码会产生更多的结果:

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

我没有官方文章或例子。。唯一的意见。

在我看来,一种无法解读的财产是一种会愤怒和困惑的野兽。

这归结为意图。一处房产上写着"我打算让消费者阅读我的内容,甚至可能给我写信"。一个名为"SetSomeAttribute"的函数声明了一个只写意图。

还有整个,这是我的数据,为什么我不能把它读回来呢。

因此,在我看来,从来没有充分的理由使用只写属性。

如果这是使代码最容易理解和维护的原因,那么拥有无getter属性并没有错。然而,这种情况的好案例可能极为罕见。

我使用过的最好的无getter属性是用于单元测试类,这些类的属性具有不可访问的setter。例如:

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

没有什么可以说你不能做到这一点,但这看起来确实有点奇怪。我会有一个私人财产和设置它的方法,即

    private string _test;
public void SetTest(string test)
{
 _test = test;
}

您必须记住属性存在的原因。它们取代了以下模式

class Foo
{
     private Bar _bar;
     public Bar GetBar() 
     { 
         return _bar; 
     }
     public void SetBar(Bar bar) 
     { 
         _bar = bar; 
     }
}

虽然拥有一个没有Setter的属性在我看来很奇怪,但我不认为拥有一个不带Get方法的Set方法看起来很奇怪。

事实上,我非常确信属性是语法上的糖,并被重写为get/set方法。或者至少非常接近。

当由于某种原因构造函数和方法注入都不适用时,它们可能被用于依赖注入。

但我无法想象这样的情况。

好吧,它们可以很好地用于对象初始化程序中的私有字段:

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

这可能是将它们作为构造函数参数写入的另一种方式:

var obj = new MyClass(value1, value2);

但是,如果有许多(可选)参数,则只写属性会很方便。

正如已经提出的,我认为答案是在问题的上下文中最有意义的。我可以想出两种替代拥有仅setter属性的方法,这可能值得一试,看看哪种最有意义:

  1. 定义一个接受单个参数的单独方法
  2. 定义一个始终返回相同值(例如null)的getter。这是否合理或令人惊讶取决于上下文

您可以拥有更整洁的属性,并且可以对其进行完全访问控制,而不需要混乱的_localVariable和属性语法。

示例:

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