【问题标题】:Is there any reason to have a property with no getter?有没有理由拥有没有吸气剂的财产?
【发布时间】:2012-03-02 15:21:04
【问题描述】:

我的经理问我使用带有 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 本身的这种用法示例?

【问题讨论】:

  • 它本身并没有什么问题,但它并不常见。如果只能设置它,为什么还要为此设置属性?
  • @KAJ 谢谢!一开始我没有在搜索中找到那个。

标签: c# properties


【解决方案1】:

官方文章: Design Guidelines for Developing Class Libraries -> Member Design Guidelines -> Property Design

不提供仅设置的属性。

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

【讨论】:

    【解决方案2】:

    考虑的问题是:有没有人有任何关于 getter-less 属性的官方文章?或者更好 - .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;属性:路径发现

    程序集:mscorlib,版本=4.0.0.0,文化=中性,PublicKeyToken=b77a5c561934e089;类型:重定向代理;属性:对象模式

    所以看起来框架很少使用它。我建议也这样做。

    编辑

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

    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
    

    【讨论】:

    • 绝妙的答案!我自己运行了这段代码并得到了相同的结果。我也向我的经理展示了;)谢谢!
    • 这些结果并不能表明该属性是否可以公开访问 - 这可能是一个有趣的指标。
    • 仅仅因为MS这样做并不意味着它是一种合理的做法。
    • @JoshuaDrake - 如果我们谈论的是 MS 对 C++ 或 JScript 的实现,我会同意,但是在谈论 .NET 语言(他们设计的)时,没有更好的来源。
    【解决方案3】:

    我没有官方文章或例子。只有意见。

    在我看来,无法读取的属性是会愤怒和困惑的野兽。

    归结为意图。一个属性说“我打算让消费者读我,甚至可能写信给我”。一个名为“SetSomeAttribute”之类的函数声明了一个只写意图。

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

    所以在我看来,使用只写属性从来都不是很好的理由。

    【讨论】:

    • 我想到的一个很好的例子是与密码有关的东西。也许有一个凭证数据结构允许您读/写用户名,但密码是仅设置的。它可以具有将其与另一个实例(即存储有正确值的实例)进行比较的内部方法,以在内部确定它是否有效,或者它可能具有公开访问哈希的选项。
    • @Servy:我不确定我是否遵循。如果一个属性是只写的,那么 NOBODY,甚至该类/结构的另一个实例都不能读取该属性。那么这种比较将如何进行呢?在这种情况下,我会选择 .SetPassword() 方法而不是只写属性,并比较公共 .Hash 属性。
    • 好吧,setter 中的代码显然需要将它存储在一些私有存储介质中,以便类可以在内部访问它;并且没有 getter 的自动生成的属性显然毫无意义(甚至可能被编译器删除)。我同意,这与使用 setPassword 方法基本相同。我并不是说这会更好,只是这是一种罕见的情况,如果我看到团队成员实施它,我不会反对它。
    • 好的。那我就为我们俩反对吧。 :-)
    【解决方案4】:

    拥有无 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; } 
        }
    }
    

    【讨论】:

    • 为类添加一些功能只是为了进行单元测试似乎是一种代码味道。
    • 我想我可以指定 _Test 类仅存在于单元测试的上下文中 - 它允许我设置对象的状态并将单个方法作为单个单元进行测试。
    • 我说的是让 MyClass.MyId 的 setter 受保护。如果这仅用于单元测试——恕我直言,这不是一个好的设计。
    • 我同意 - 我不得不这样做的时候,protected set 已经超出了我的控制范围(例如外部 dll),扩展类是使单元测试成为可能的最佳选择。
    【解决方案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; 
           }
      }
      

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

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

      【讨论】:

      • 我也有同样的看法。没有GetBarSetBar 似乎很正常,但这就是编译器对C# 属性所做的事情!
      【解决方案7】:

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

      但我无法想象这种情况。

      【讨论】:

        【解决方案8】:

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

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

        这可能是一种将它们写为构造函数参数的替代方法:

        var obj = new MyClass(value1, value2);
        

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

        【讨论】:

          【解决方案9】:

          正如已经建议的那样,我认为答案是在问题的上下文中最有意义的。我可以想到两个替代方法,而不是只有一个 setter 属性,这可能值得一试,看看哪个最有意义:

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

          【讨论】:

            【解决方案10】:

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

            例子:

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

            【讨论】:

            • -1:不相关的答案 - 问题是“没有 get 的财产有用吗”,答案是“看看如何获​​得”。
            猜你喜欢
            • 2011-12-21
            • 2021-06-01
            • 2021-04-02
            • 2020-09-30
            • 1970-01-01
            • 1970-01-01
            • 2015-05-04
            • 2020-05-11
            • 2021-02-27
            相关资源
            最近更新 更多