C#-为什么不鼓励在字段上使用前缀?


19

过去,我们使用匈牙利符号。现在将其视为passé,并且在大多数情况下,我不再使用它,但是我仍然发现该m_前缀用于表示成员字段。

对我来说,如果我正在阅读别人的代码,就会看到以下内容:

count = 3;

我假设这count是该函数的局部变量,并且我正在做将在该函数的其他地方使用的操作。如果我看到这个:

m_count = 3;

我立即意识到我正在更新对象的状态。

Microsoft风格指南说这是做事的错误方法。我应该像在函数中定义的临时变量一样命名我的非公共字段。不m_,甚至没有简单的下划线。

是的,我们可以定义自己的编码样式,但是接下来我必须努力使用各种静态代码分析工具,以使他们相信我们的处事方式还可以。

我很高兴能够更改为Microsoft风格,但是我想知道为什么事情是这样。

为什么现在不能分辨一个变量是函数本地变量还是成员变量,这为什么认为很糟糕?

PS这非常类似于c#领域和方法前面关于“ this”关键字的当前最佳实践是什么?,但我问的m_不是this.

PPS另请参见Microsoft为什么使参数,局部变量和私有字段具有相同的名称命名约定?


9
C#没有this关键字吗?您不能使用引用成员this,例如this.count吗?
FrustratedWithFormsDesigner

3
m_前缀是一种C ++形式,它避免了字段和方法之间的名称冲突。在C#中,这不是问题,因为方法名称应以大写字母开头,字段以小写字母开头。
阿蒙(Amon)

4
如果您要在2017年打印代码,那么您在做错什么。在其他两种情况下,应该很明显的是count,由于没有在方法中声明,所以它并非没有出现。坦白说,“ IDE之外”的说法确实很弱。尤其是因为甚至有代码检查工具都可以在IDE中使用。
安迪


Answers:


19

当他们使用Windows API时,Microsoft建议使用匈牙利符号,使用'm_'以及'i','sz'等。当时,这很有用,因为IDE不够强大足以向您显示此信息。

另一方面,C#从一开始就具有非常强大的IDE。

因此,他们在Microsoft的C#命名准则中为公共静态或受保护字段提出了建议:

 DO use PascalCasing in field names.
 DO name fields using a noun, noun phrase, or adjective.
X DO NOT use a prefix for field names.

现在您可以将鼠标悬停在其上,或按CTRL + K + W并获取有关该成员的所有信息,Microsoft便确定前缀是错误的形式;它们是已解决问题的手动解决方案。

专用字段专门排除在指南之外,但是Microsoft似乎得出的结论是,即使对于内部进行的专用字段也是这种情况,您可以查看是否将Resharper指向.NET 4.5或.NET Core。

使用您的工具,不要手动进行。


2
与Andy的答案相同...是的,但是...在很多情况下,我正在IDE之外查看代码。示例-(1)合并工具。(2)代码审查工具。(3)(空白)打印输出。
Betty Crokker,

安迪在字面上贴出我的那一刻。我单击“添加答案”时看到“ 1条新评论”弹出。至于这些工具,其中两个已经在IDE中。打印输出...您为什么要这样做?
凯文·费

1
我最初的问题仍然没有答案……是的,微软表示不要使用m_,工具制造商也照此进行,但这并不是使用m_的技术原因,这就是通常所说的“呼吁授权”。我看到不使用m_的唯一技术原因来自Timothy Truckle,但我不明白...
Betty Crokker

8
@BettyCrokker因为m_绝对没有任何价值。IDE会告诉您是否成为成员,因此m_是多余的。为什么要固定在m_上?为什么不l_(对于本地)?为什么不使用afsdfjdskfjas_?我要否决这个问题,因为无论如何这是一个愚蠢的宗教论点。做你想做的。你也敲方针说:“现场命名准则适用于静态公共和保护的领域内部和私有字段_not_通过的指导方针所覆盖,公共或受保护的实例字段不是由成员设计准则允许的。”
安迪

1
@BettyCrokker好的,但是您正在询问使用该假设制定的准则。您还抱怨静态工具也可能使用该假设,因为“没人”(在可接受的统计误差范围内)会打印出他们的C#代码。我不知道您使用什么统计分析工具,但是Resharper非常具体:专用字段为“ _lowerCase”。任何类型或“大写”的公共成员,而本地人是“小写”。无论如何,保存“ m”实际上看起来非常不错。
凯文·费

17

C#没有任何全局变量,因此只剩下局部变量和类字段。

而且,如果您有太多的局部变量,那么您就无法跟踪标识符是否为1,那么您肯定会最严重地违反了管理复杂性的准则之一。

尝试提取参数对象,尝试将函数拆分为多个简单的对象,除非您不喜欢细节和复杂性以及不考虑可维护性,否则您肯定应该重构代码。

既然我们已经确定“此成员”装饰的目的不再成立,因为功能范围应该太小并且不存在全局范围,那么这些标记有一个缺点:它们抑制了将参数/局部变量移至成员,或者反之。


一旦插入它,它也可以是类或名称空间。
CodesInChaos

3
我认为这并不能真正回答问题。
弗拉基米尔·斯托基奇(Fladimir Stokic)

5
@FrankHileman其实是。它解释了为什么没有充分的理由来丑化一个名字。
重复数据删除器

2
“丑陋”?在这种情况下,这是一个仅供参考的问题。每个人都可能对丑陋有不同的看法。有理由偏爱一种约定而不是另一种约定,但是在这种情况下没有标准约定。
Frank Hileman

1
@Deduplicator的美丽在情人眼中。我曾经认为丑陋的约定,但现在对它很有用。
Frank Hileman

11

开发人员为什么要避免使用using namespace指令在名称空间前添加前缀?

System.DateTime() 比这更明确 DateTime()。。它准确地告诉我我在处理什么。仅仅是因为我们是懒惰的打字员吗?

不。我们是懒惰的打字员,但这不是原因。

我们更喜欢使我们忽略细节而专注于抽象的编码样式。强迫名称传达结构细节令人分心。当我在研究复杂的算法时,我不想让名字一直提醒着我正在使用的东西的每一个细节。我只需要这个名字,以免使我感到困惑TimerDateTime。我会担心这些在其他地方是否兼容。

这也是您不希望团队中有两个人叫乔恩的原因。它们具有姓氏这一事实并不是给它们加上前缀或后缀的原因。我只想称呼你为斯基特。还有什么是彻底的痛苦。


2
这似乎无法回答问题。.net中的私有字段没有一般的首选项或约定。
Frank Hileman

12
@FrankHileman通常,我们更喜欢好名字。约定不会创造好名声。约定创建的名称类似于McOhPlease Von StopItAlreadyStine。
candied_orange

约定只是为了更轻松地使用不同的开发人员代码。您的答案确实针对匈牙利语,但是对于私有字段没有约定,因此问题基于错误的假设。
Frank Hileman

7

让我们扩大一点。

有3种常见的前缀样式,在c#代码中偶尔会遇到所有这些样式:

  • m_
  • _
  • 这个。

此类前缀通常用于类的私有实现中。Microsoft的建议主要是关于课程的公开部分。

使用m_over 的优点_是,如果您使用c#以及不允许使用_前缀的语言,则前缀在整个代码库中可以相同。* m_over 的优点this.是它更短并且不会偶然忘记前缀。

_over 的优点m_是它更短,并且以前缀开头的所有内容都在顶部按工具按字母顺序进行排序。_over 的优点this.是它更短,并且您不会偶然忘记前缀。

this.over m_and 的优点_是,您可以在公认_m_不通用的惯例的代码库中使用它。这也意味着没有人需要了解_or m_约定,这可以使事情变得更简单。不利的一面是,由于编译器不关心该前缀,因此this.有时会丢失该前缀,因此其可靠性不如指标高。


*一旦您开始访问_从C ++ / CLI 使用的C#类(实际上确实不应该使用_),您会注意到,就通用前缀达成共识要比应有的复杂。决定不加前缀可以消除这种噪音。将此与Deduplicator的答案结合起来,我将其解释为“前缀的优势几乎可以忽略不计”,它为我们提供了:“使用前缀时存在一个小问题,而且上行空间很小,因此这是一个有效的选择,使用它们”。


关于此的stackoverflow还有一个较旧的问题


1
不错的比较。但是,我发现不使用任何前缀在实践中效果很好。
Phil1970年

6

应当注意,MS样式指南仅讨论公共和受保护的方法和变量。即其他开发人员将查看他们是否使用您的库。

其想法是为使用Microsoft库的开发人员提供标准的外观。

您可以随意在私有变量或局部变量上使用任何样式。

前面已经说过,由于多种原因,一般不建议使用可变前缀

  • 它们可能是错误的,因此具有误导性
  • 如果要使用变量类型作为前缀,则不清楚如何处理已创建的类。
  • 有许多约定,但它们并不总是一致的。您发现有帮助的其他开发人员可能会发现无用
  • 对于成员变量,可以使用此“ this”。表示范围的关键字,编译器将强制执行。

2
最初,我对字段完全不使用前缀。后来,我切换到使用单个“ _”字符,因为我看到其他人在使用它,因为当您使用IDE下拉列表框时,它将所有字段移到按字母顺序排列的成员列表的顶部。在阅读由许多不同开发人员编写的代码时,缺乏标准约定可能会很烦人。
Frank Hileman '17

一段时间后,您会看到很多不同的样式,而只是忽略它们。如果您试图强制实施一种样式,那么您将不断重构所有内容
Ewan

4
如果对大多数事物应用“它们可能是错误的,从而产生误导”,您很快就会发现您不应命名变量或函数-毕竟它们可能是错误的!我猜您的第二个要点应该是“您尚未创建的类”?否则我只是听不懂...在任何情况下,听起来似乎非官方字段都带有一个下划线前缀是一种非正式的标准,这可能就是我们要走的方向。
Betty Crokker,

有些事情是由编译器检查的,因此m_count可能不是成员变量,但是this.count将始终为
Ewan

很多人使用_,但是事情会随着时间而改变,您会发现去年写成“最佳实践”的代码与今年的惯例不符
Ewan

4

对我来说,如果我正在阅读别人的代码,就会看到以下内容:

count = 3;

我假设'count'是该函数的局部变量,并且我正在做将在该函数的其他地方使用的操作

如果您正在阅读尊重C#样式约定的源代码,那么您将完全正确在这样的代码中:

this.count = 3;

为字段分配一个值。

count = 3;

为局部变量分配一个值。

因此,C#的样式约定清晰明确。它们强迫您不要仅仅因为不需要前缀不要使用任何前缀

至于作者决定改用其个人约定的源代码,您应该亲自询问他们为什么选择使用它们。如果他们不使用下划线之类的前缀,而不使用下划线,则不仅this.会导致难以阅读代码,而且还会导致需要附加约定的情况:

public class Hello
{
    private string greetings;

    public Hello(string greetings)
    {
        greetings = greetings; // Wait, what is that?!
    }
}

C#没有这个关键字吗?您不能使用此引用成员吗,例如this.count?

是的,但是(1)输入更多,(2)容易忘记。如果该字段名为m_count,则无需记住键入此内容。

但是,您在这里错了。

当您在记事本中编写代码时,键入更多内容。使用具有自动完成功能或更佳的IntelliSense功能的IDE,this.count和之间的比较m_count就不那么明显了。请注意键入下划线所需的Shift+ -

至于“容易忘记”,这再次仅与记事本用户有关。不仅StyleCop和Resharper不会让您忘记this.需要时放的地方,而且经过几个小时的编程,this.在需要时放的地方已成为一种习惯。


6
我发现this仅在需要时才使用它会导致感觉确实不一致,并且使我分心。如果您要使用this前缀而不是前缀,我相信您应该始终使用它。在这种情况下,它成为前缀,您不妨使用_
RubberDuck

1
RubberDuck是正确的。“这个。” 是前缀,尽管它确实具有编译器含义。
Frank Hileman '17

4

“成员”前缀是一种实现细节。它使您的注意力从问题领域转移到了技术解决方案领域,这使您在考虑可能的重构时偏向于头脑。- 我

你能进一步解释吗?您看到的是我为什么不使用前缀并且我不理解它的唯一原因……(我的意思是,其他人在说的是我不必使用前缀的原因,这和为什么我不应该不一样)–贝蒂·克罗克(Betty Crokker)

我的意思是扩展@CandiedOrange和@Ewan的答案。

前缀使重构更加困难

随着代码的发展,变量和方法可能会改变其范围。例如。您可以创建一个私有方法,然后发现它也应可用于其他(新)类。方法参数和局部变量可能会发生类似的情况。

假设您创建了一个计税计算器。根据变量的租约可见性范围的原理,您可以从一种将税率基值作为参数的方法开始:(
示例可能看起来像Java-ish ...)

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
} 

接下来,您必须实施反向操作,并且根据TDD,您将以最小的努力做到这一点:

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxRateInpercent, double p_TaxedValue){
     return p_TaxedValue/((100+p_TaxRateInpercent)/100);
   }
} 

下一个TDD希望您将代码重构为更简洁的代码,以减少这两种方法的参数列表。
(是的,您将首先提取加倍的计算,但是请允许我理解……)
显而易见的解决方案是将税率作为构造函数参数传递给成员变量。

class TaxCalculator{
   double m_TaxRateInpercent;
   TaxCalculator(double p_TaxRateInpercent){
     m_TaxRateInpercent = p_TaxRateInpercent;
   }
   double Calculate(double p_BaseValue){
     return (100+m_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxedValue){
     return p_TaxedValue/((100+m_TaxRateInpercent)/100);
   }
} 

如您所见,我们必须更改现有方法的所有行(我们过去曾经p_TaxRateInpercent精确的任何行)。

问题是,您在整个类中都没有IDE的支持来进行重命名。它搜索/替换的唯一帮助,这也将更改构造函数或意外包含字符串的任何部分p_TaxRateInpercent
您的IDE可能会为未定义的变量名提供...重构的更改,但这可能仅限于方法的范围

如果没有前缀,则仅方法签名会更改。完全不需要重命名。


更改时前缀混乱的SCM历史记录

另外,SCM记录中虽然逻辑没有变化,逻辑变化前缀的变化!SCM的历史充斥着这项技术更改,隐藏了重要的内容,并增加了与其他(真实逻辑)更改发生冲突的风险。


1

我为所有成员变量使用_前缀。我讨厌'this'前缀,因为尽管有人声称这是正确的处理方式-它给您的代码库增加了很多噪音/混乱。单个下划线也是Microsoft示例中的常见做法。

因此,在您的示例中,我将有:

int _count = 10;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.