阻止访问私有成员变量?强制使用公共属性?

12

我正在使用.NET 2.0,因此无法使用自动属性。因此,我必须采用以下方式编写私有变量和公共属性的代码。

private string m_hello = null;

public string Hello
{
     get{return m_hello;}
     set{m_hello = value;}
}

对于上述私有/公有成员所在类的方法,是否有办法限制对私有变量的访问?我不喜欢只能使用m_hello或Hello。

谢谢。


11
需要注意的一点是 - 即使您的目标是 .NET 2.0,如果您使用 C# 3 来实现,仍然可以使用自动属性。它们不需要任何框架支持。 - Jon Skeet
@Jon -- 这可能是足够重要的信息,值得回答而不是评论。 - tvanfosson
3
虽然与主题不太相关,但我很惊讶人们仍在使用"m_"。这不仅被建议避免使用(我个人更喜欢使用前导"_"来表示私有变量),而且它看起来很丑,尽管这可能是个人观点。以下是一篇关于.NET命名约定和编程标准的文章:http://10rem.net/articles/net-naming-conventions-and-programming-standards---best-practices - Fusyion
我在这里问了一个类似的问题:https://dev59.com/4U7Sa4cB1Zd3GeqP6s1i - dlras2
@Kevin:不过你得承认,这是我们可能讨论的最主观的话题之一。这并不像调用代码曾经看到这些变量名一样。一旦你至少通过名称决定以某种方式区分私有成员和公共成员,那么这完全是个人(和团队)审美的问题。 - Dan Tao
显示剩余5条评论
9个回答

13

正如其他人所建议的,这应该是一个答案...

即使针对.NET 2.0,您仍然可以在C# 3中使用自动属性,以及相当多其他C# 3功能。与(例如)表达式树不同,自动属性除了[CompilerGenerated]属性(在.NET 2.0中引入)之外,不需要来自CLR或框架的特殊内容。

因此,如果您使用的是VS2008或VS2010,则值得使用自动属性。

但值得一提的是,我也想要这个能力。 我希望能够在属性内部限定变量的范围:

 public string Name
 {
     private string name;
     get { return name; }
     set { name = value; }
 }

我认为这有点像将私有变量设置为只读 - 对客户端没有影响,但有助于在类代码内部执行正确性检查。


3
我完全同意私有作用域属性变量的使用。我非常希望能够在属性方法中编写任意代码并且将访问限制在属性上,以便我不会无意中避开getter/setter代码中的“规则”。 - tvanfosson
1
顺带提一下,我也想要不可变的自动属性(可通过构造函数或初始化语法进行设置),然后只能从那里获取。由编译器生成的readonly字段支持。 - Jesse C. Slicer

8
您可以通过继承来实现这一点:
abstract class A // A is not instantiatable due to being abstract
{
    private string m_hello = null;

    public string Hello
    {
         get{return m_hello;}
         set{m_hello = value;}
    }
}

class B : A
{
    // B now cannot access the private variable, use B in your code instead of A
}

我并不是在声称这是好的。只是它是可以做到的。


6

除了遵循自己的约定并执行此操作之外,没有其他方法可以做到这一点。如果您确实需要浏览公共属性,请注意。

我不明白为什么您需要/想要这样做,因为既然它是您的内部类,您可以控制代码并定义其使用方式/方式,所以不应该有任何问题。


哇!抱歉 @Mitchel - 我不小心编辑了错误的帖子。对此感到抱歉。 - Marc Gravell
@Marc - 你被可爱的随机排序特性咬了吗?我经常遇到这种情况,自动假设我的问题在编辑(或提交)后仍然处于同一位置,并盲目地点击编辑按钮。当该位置的答案以几乎相同的文本开头时,这并没有帮助。 - tvanfosson

4

没有。类内的任何方法都可以访问两者。

您的团队应该统一使用哪种(属性或私有变量)。

一旦您决定使用哪种,您可以尝试使用自定义的FxCop规则来强制执行标准。


1
不,基本上不需要。嗯,你可以通过使用[Obsolete]#pragma来处理编译器警告,但那会过度了。
你可能可以通过工具来实现,但最终你需要相信人们不会做蠢事。毕竟,你是否有特殊规定:
while(true) { }

还是你只是觉得这很“愚蠢”?;p


1
while(true) 加上循环体内的 break 是一个非常合法的编程结构。比那更糟糕的事情还有很多。 - ToxicAvenger
1
@ToxicAvenger - 但是没有 break - Marc Gravell
这条推理线可以证明任何事情。 - Micha Wiedenmann

1

就我个人而言,我认为在类内部访问私有成员没有任何问题。事实上,这是我通常的做法(除非属性的getter/setter中有逻辑需要一直使用)。

对我来说,这很合理:类内部的代码构成了该类的实现;为什么要将实现隐藏起来呢?

以下是我的一个例子。假设我有一个成员变量m_denominator,并且我希望它永远不为零:

private int m_denominator = 1;
public int Denominator
{
    get { return m_denominator; }
    set
    {
        if (value == 0)
            throw new ArgumentException("Denominator must not be zero.");
        m_denominator = value;
    }
}

我可能会对自己说:“好的,在这个类中,无论我在哪里设置这个值,我都应该使用分母来确保我没有将其设置为零。” 但是我完全控制着我要将分母设置为什么——我在类内部!这种情况下分母属性的逻辑点是保护类免受客户端代码设置的无效值。在类本身的实现中将内部状态设置为某些无效值是没有任何借口的。

当然,这不是绝对的规则。当在类中使用属性进行逻辑处理作为一种保护措施时,这可能是明智的选择;实际上,我只是认为从类内部访问私有成员并不是错误的


2
当你在6个月后回到代码中添加重要逻辑到属性设置器时会发生什么?你的类内部代码现在将绕过新实现。 - Dr Herbie
3
我更倾向于默认使用属性,因为我不想跟踪属性在哪些地方执行特殊操作。优化器通常会处理方法调用的优化,至少对于简单的访问器而言。只有在我知道属性执行某些“特殊”操作并且我想为特定目的避免这种情况时,我才会使用属性。 - tvanfosson
2
@Dr Herbie:但是根据我的经验,你可能会向属性添加逻辑,但不希望该逻辑在类内的每次访问中执行——只在来自外部代码的访问中执行。我确实与一些开发人员合作过,他们向其属性添加了逻辑,突然发现他们的某些代码被执行的频率比他们预期的要高得多。这显然是一个判断的问题;我只是指出隐藏实现细节与隐藏它本身之间存在差异。 - Dan Tao
2
我觉得这是高度可疑的推理。通常,你会在属性设置器中放入额外的代码来执行某些依赖或业务规则。我的经验是,即使在类中,你通常也想避免这种情况。当然有例外情况--在这种情况下,我会引用备份属性--但因为它们是例外情况,在默认情况下我使用属性而不是另一种方式。参考您的示例,我会说在初始化器中直接使用字段是可以的,但在类的任何其他地方,您真的需要使用属性。 - tvanfosson
@tvanfosson:嘿,我并不是说你错了。我的观点基本上是这样的:OP似乎正在寻找一种方法来防止从实现中访问私有成员。我认为:这不一定是你需要做的事情。至于什么样的逻辑应该或不应该放入属性getter/setter中,那是另一个争论。无论如何,我们似乎都愿意承认有时候一种方法是合理的,而有时候另一种方法更有意义;这就是我真正想要争论的--不是哪种方法是例外,哪种方法是规则。 - Dan Tao

1
你应该仅通过公共的 Hello 属性访问该属性。这就是采用此模式的原因。如果你为 get 或 set 添加任何功能,并访问私有实例,那么你将在代码中引入错误。但答案是否定的,当某人在更改你的代码时,你不能阻止他们在类内部调用 Private。

可以说这个问题有两种方式处理:你可能想直接引用支撑字段,以此避免受到公有属性的任何添加影响。然而在这方面最好始终保持一致,并且可能将跳过属性的支撑变量明确标识出来是个好主意。例如,你可以在它们的后缀中加入“_Prop”或类似的东西。 - Steven Sudit
我更喜欢一致性,只是想指出模式的原因。每个单独的解决方案都有其原因,但并不意味着它是最佳实践。 - CkH

0

我相信在 C#10,或者至少有一个提议能使用半自动属性。

现在我们可以使用field关键字代替后备属性字段。

所以这个:

private int _theNumber;
public int TheNumber 
{
    get => _theNumber;
    set => _theNumber = value;
}

变成这样:

public int TheNumber
{
    get => field;
    set => field = value;
}

在我提供的例子中,自动属性可能更有意义,但我想展示一个简单的例子。


0
如果你发现自己想要隐藏一些细节,那可能是你的类有太多的职责的代码异味。考虑将其中一个职责提取到一个新的类中。

1
有时候问题不在于“你自己”,而是团队中的其他成员无法遵守规则。我知道,理论上一切都有简单的解决方案,但生活是艰难的... - bpiec
1
@JoeWhite:这是一个有趣的观点。然而,将类分解到几乎每个类都具有微不足道的职责的程度上,在理论上听起来不错,但在实践中可能会成为一场噩梦,难以理解。 - oliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接