为什么在C ++类中使用成员variables的前缀
许多C ++代码使用语法约定来标记成员variables。 常见的例子包括
- 公共成员的m_ memberName (根本用于公共成员)
- 私人成员或所有成员的_ memberName
其他人试图在使用成员variables时强制使用this-> member 。
根据我的经验,大多数较大的代码库不能一致地应用这些规则。
在其他语言中,这些约定远没有那么普遍。 我偶尔会在Java或C#代码中看到它。 我想我从来没有见过Ruby或Python代码。 因此,现代语言似乎有一种趋势,不使用特殊的标记来表示成员variables。
这个惯例今天在C ++仍然是有用的,或者它只是一个时代错误。 特别是因为它在图书馆中的使用如此不一致。 没有其他语言显示,没有成员前缀可以做?
你必须小心使用一个领先的下划线。 大写字母之前的下划线是保留的。 例如:
_Foo
_L
都是保留字
_foo
_l
不是。 在其他情况下,不允许在小写字母前加下划线。 在我的具体情况中,我发现_L碰巧被Visual C ++ 2005保留,冲突创build了一些意想不到的结果。
我很关注标记局部variables是多么有用。
下面是关于哪些标识符被保留的链接: 关于在C ++标识符中使用下划线的规则是什么?
我都赞成前缀做得好 。
我认为(系统)匈牙利符号负责前缀得到的大多数“坏的说唱”。
这种表示法在强types语言(如C ++“lpsz”)中基本上是毫无意义的,它告诉你,你的string是一个长指针,指向一个nul结束的string,当:分段体系结构是古老的历史,C ++string是由nul-terminated char数组,并且知道“customerName”是一个string并不是那么难。
然而,我确实使用前缀来指定variables的使用 (本质上是“应用匈牙利语”,尽pipe我更喜欢避免使用匈牙利语这个词,因为它和匈牙利语系有不良和不公平的联系),这是非常方便省时的 减less错误的方法。
我用:
- 米为会员
- c为常数/只读
- p指针(和pp指针指针)
- v为volatile
- s为静态
- 我为索引和迭代器
- e事件
在我希望使types清晰的情况下,我使用标准后缀(例如List,ComboBox等)。
这使得程序员在看到/使用variables的时候知道variables的用法 。 可以说,最重要的情况是指针的“p”(因为var的用法变化为var->而且你必须更加小心指针 – NULL,指针算术等等),但其他的都非常方便。
例如,您可以在一个函数中以多种方式使用相同的variables名称(这里是一个C ++示例,但它同样适用于许多语言)
MyClass::MyClass(int numItems) { mNumItems = numItems; for (int iItem = 0; iItem < mNumItems; iItem++) { Item *pItem = new Item(); itemList[iItem] = pItem; } }
你可以在这里看到:
- 成员和参数之间没有混淆
- 索引/迭代器和项目之间没有混淆
- 使用一组清晰相关的variables(项目列表,指针和索引),避免了像“count”,“index”这样的通用(含糊)名称的许多缺陷。
- 与“itemIndex”和“itemPtr”之类的替代方法相比,前缀可以减lessinput(缩短,自动完成更好)
“iName”迭代器的另一个重点是,我从来没有索引错误索引的数组,如果我在另一个循环内复制一个循环,我不必重构一个循环索引variables。
比较这个不现实的简单例子:
for (int i = 0; i < 100; i++) for (int j = 0; j < 5; j++) list[i].score += other[j].score;
(这是很难阅读,并经常导致使用“我”在“j”的意图)
有:
for (int iCompany = 0; iCompany < numCompanies; iCompany++) for (int iUser = 0; iUser < numUsers; iUser++) companyList[iCompany].score += userList[iUser].score;
(这是更可读性,并消除了索引的所有困惑。在现代IDE中的自动完成,这也是快速和容易input)
下一个好处是代码片段不需要任何上下文来理解。 我可以将两行代码复制到电子邮件或文档中,阅读该代码段的任何人都可以知道所有成员,常量,指针,索引等之间的区别。我不必添加“哦,要小心,因为'data'是一个指向指针的指针“,因为它被称为'ppData'。
出于同样的原因,我不必为了理解而忽略一行代码。 我不必search代码来查找“数据”是本地,参数,成员还是常量。 我不必将手移动到鼠标上,所以我可以将指针hover在“数据”上,然后等待提示(有时从不出现)的工具提示popup。 所以程序员可以更快速地阅读和理解代码,因为它们不会浪费时间search上下或等待。
(如果你不觉得你浪费时间上下寻找工作,找一些你一年前写过的代码,而没有看过,打开这个文件并跳过一半,而不去阅读它。远远的你可以从这个angular度读取,然后你不知道是否是某个成员,参数或本地的东西。现在跳转到另一个随机的位置……这是我们一整天都在做的事情,当我们单步穿过别人的代码或试图了解如何调用他们的function)
'm'前缀也避免了(恕我直言)丑陋而罗嗦的“this->”符号,以及它所保证的不一致性(即使你小心,通常也会得到'this-> data'和'data'在同一个类中,因为没有强制名称的一致拼写)。
'这个'符号是为了解决歧义 – 但为什么有人会刻意编写可能不明确的代码? 歧义迟早会导致错误。 而在某些语言中,“这个”不能用于静态成员,所以你必须在你的编码风格中引入“特殊情况”。 我更喜欢有一个适用于任何地方的简单编码规则 – 明确的,明确的和一致的。
最后一个主要的好处是智能感知和自动完成。 尝试在Windows窗体上使用Intellisense来查找事件 – 必须滚动浏览数百个不需要调用才能查找事件的神秘基类方法。 但是,如果每个事件都有一个“e”前缀,它们将自动被列在“e”下的一个组中。 因此,前缀可以将intellisense列表中的成员,常量,事件等进行分组,从而更快,更轻松地find所需的名称。 (通常,一个方法可能有20-50个值(locals,params,members,consts,events),它们的范围是可以访问的,但是在input前缀之后(我现在想使用一个索引,所以我input'i。 ..'),我只给出了2-5个自动完成选项,“额外打字”的人属于前缀和有意义的名字,大大减less了search空间,显着加快了开发速度)
我是一个懒惰的程序员,上面的约定为我节省了很多工作。 我可以更快地编写代码,而且我犯的错误要less得多,因为我知道每个variables应该如何使用。
反对的论点
那么,缺点是什么? 针对前缀的典型参数是:
-
“前缀scheme是坏的/邪恶的” 。 我同意“m_lpsz”之类的思想很糟糕,完全没有用处。 这就是为什么我build议使用精心devise的符号来支持您的要求,而不是复制一些不适合您的上下文的东西。 (为作业使用正确的工具)。
-
“如果我改变某些东西的用法,我必须重新命名” 。 是的,当然你是这样做的,这就是重构的全部内容,为什么IDE有重构工具来快速无痛地完成这项工作。 即使没有前缀,改变variables的用法几乎肯定意味着它的名字应该改变。
-
“前缀只是混淆了我” 。 每个工具一样,直到你学会如何使用它。 一旦你的大脑已经习惯了命名模式,它会自动过滤信息,你不会介意前缀已经存在了。 但是,在你真正变得“stream利”之前,你必须坚定地使用这样一个或两个scheme。 这就是当很多人看旧的代码,并开始怀疑他们如何pipe理没有一个好的前缀scheme。
-
“我可以看看代码来解决这个问题” 。 是的,但是当你的眼睛已经集中在正确的位置上的时候,你不需要浪费时间寻找代码中的其他地方,或者记住它的每一个细节。
-
(其中一些),只要等待工具提示popup我的variables就可以find这些信息 。 是。 在支持的地方,对于某些types的前缀,当你的代码干净地编译,在等待之后,你可以通读一个描述并find前缀将立即传达的信息。 我觉得前缀是一个更简单,更可靠和更有效的方法。
-
“这是更打字” 。 真? 一个完整的angular色更多? 或者是 – 使用IDE自动完成工具,通常会减less键入,因为每个前缀字符都会显着缩小search空间。 按下“e”,你的class级中的三个事件就会以智能感知的方式popup。 按“c”,列出五个常量。
-
“我可以用
this->
而不是m
” 。 那么,是的,你可以。 但是,这只是一个更丑陋,更详细的前缀! 只有它承担了更大的风险(特别是在团队中),因为编译器是可选的 ,因此它的使用经常是不一致的。m
另一方面是简短的,清晰的,明确的而不是可选的,因此使用它很难做出错误。
我通常不使用成员variables的前缀。
我曾经使用m
前缀,直到有人指出“C ++已经有一个标准的成员访问前缀: this->
。
所以这就是我现在使用的。 也就是说, 当有歧义时 ,我添加this->
前缀,但通常不存在歧义,我可以直接引用variables名称。
对我而言,这是两全其美的。 我有一个前缀,我可以使用,当我需要它时,我可以随时离开它,只要有可能。
当然,显而易见的反作用是“是的,但是你不能一眼就看出一个variables是不是集体成员”。
对于我所说的“那么怎么样呢?如果你需要知道的话,你的class级可能状态太多了,或者function太复杂了”。
在实践中,我发现这个工作非常好。 作为一个额外的好处,它允许我轻松地将一个局部variables提升为一个类成员(或其他方式),而不必重新命名它。
最重要的是,它是一致的! 我不必做任何特别的事情,也不需要记住任何惯例来保持一致性。
顺便说一下,你不应该为你的class级成员使用领先的下划线。 你会不舒服地接近实现保留的名字。
标准保留所有名称,以双下划线或下划线开头,后面跟大写字母。 它还保留全局名称空间中以单个下划线开头的所有名称。
所以一个带有下划线的小写字母的后面跟一个小写字母是合法的,但是你迟早要用大写字母来标识一个标识符,否则就会违反上述规则中的一个。
所以避免前导下划线比较容易。 如果要在variables名称中编码范围,请使用后缀下划线或m_
或m
前缀。
我更喜欢后缀下划线,就像这样:
class Foo { private: int bar_; public: int bar() { return bar_; } };
最近我一直倾向于使用m_前缀,而不是根本没有前缀,原因不在于标记成员variables很重要,而是为了避免含糊不清,比如说你的代码是这样的:
void set_foo(int foo) { foo = foo; }
原因不起作用,只允许一个foo
。 所以你的select是:
-
this->foo = foo;
我不喜欢它,因为它导致参数阴影,你不再可以使用
g++ -Wshadow
警告,它也更长时间键入然后m_
。 当你有一个int foo;
时,你仍然会遇到variables和函数之间的命名冲突int foo;
和一个int foo();
。 -
foo = foo_;
或者foo = arg_foo;
使用了一段时间,但它使参数列表丑陋,文档不应该在执行中处理名称歧义。 variables和函数之间的命名冲突也存在于这里。
-
m_foo = foo;
API文档保持干净,你不会在成员函数和variables之间产生歧义,而且它的types越短
this->
。 唯一的缺点是它使得POD结构变得丑陋,但是由于POD结构首先不受名称歧义的困扰,所以不需要与它们一起使用。 具有独特的前缀也使得一些search和replace操作更容易。 -
foo_ = foo;
m_
大多数优点都适用,但是出于美学的原因,我拒绝它,尾随或者前导的下划线只会使variables看起来不完整和不平衡。 只是看起来更好。 使用m_
也是可扩展的,因为你可以使用g_
作为全局variables,s_
作为静态variables。
PS:在Python或Ruby中看不到m_
的原因是因为两种语言都强制使用自己的前缀,所以Ruby使用@
作为成员variables,而Python需要self.
。
我不能说这是多么的宽泛,但个人来说,我总是(而且总是)以'm'作为前缀。 例如:
class Person { .... private: std::string mName; };
这是我使用的唯一前缀forms(我非常反匈牙利符号),但多年来它一直是我的代名词。 顺便说一下,我通常厌恶在名称(或其他任何地方)中使用下划线,但是对于预处理macros名称是个例外,因为它们通常都是大写的。
在阅读一个成员函数时,知道谁拥有每个variables对于理解variables的含义是绝对必要的。 在这样一个函数中:
void Foo::bar( int apples ) { int bananas = apples + grapes; melons = grapes * bananas; spuds += melons; }
看到苹果和香蕉来自哪里很容易,但是葡萄,瓜类和短脚呢? 我们应该看看全球命名空间吗? 在类声明中? variables是此对象的成员还是此对象的类的成员? 不知道这些问题的答案,你不能理解的代码。 而在一个更长的函数中,即使像苹果和香蕉这样的局部variables的声明也可能在混洗中失去。
为全局variables,成员variables和静态成员variables(可能分别为g_,m_和s_)预先添加一个一致的标签可以立即说明情况。
void Foo::bar( int apples ) { int bananas = apples + g_grapes; m_melons = g_grapes * bananas; s_spuds += m_melons; }
这些可能需要一些习惯 – 但是,那么在编程中没有? 有一天,甚至{和}看起来很奇怪。 一旦你习惯了它们,它们可以帮助你更快地理解代码。
(使用“this->”代替m_是有道理的,但是更加冗长和视觉上的破坏性,我不认为它是标记成员variables所有用法的好select。
对上述论点的一个可能的反对意见是把这个论点扩大到types。 了解variables的types“对于理解variables的含义是绝对必要的”也许是事实。 如果是这样,为什么不为每个标识其types的variables名添加前缀? 用这个逻辑,你最终得到匈牙利符号。 但是很多人发现匈牙利的记号是辛苦的,丑陋的,无益的。
void Foo::bar( int iApples ) { int iBananas = iApples + g_fGrapes; m_fMelons = g_fGrapes * iBananas; s_dSpuds += m_fMelons; }
匈牙利人告诉我们一些关于代码的新东西。 我们现在明白,在Foo :: bar()函数中有几个隐式转换。 现在代码的问题是,匈牙利前缀添加的信息的价值相对于视觉成本是很小的。 C ++types的系统包含许多function,可以帮助types一起工作,或者引发编译器警告或错误。 编译器帮助我们处理types – 我们不需要这样做。 我们可以很容易地推断出Foo :: bar()中的variables可能是数字的,如果这就是我们所知道的,这对于获得对函数的一般理解已经足够了。 因此,了解每个variables的精确types的值相对较低。 然而,像“s_dSpuds”(甚至只是“dSpuds”)这样的variables的丑陋性是很好的。 因此,成本效益分析反对匈牙利语言,而g_,s_和m_的好处压倒了许多程序员眼中的成本。
我不认为一种语法对另一种语法有实际价值。 就像你提到的,这一切都归结为源文件的统一性。
我发现这样的规则有趣的唯一的一点是,当我需要两个名字相同的东西,例如:
void myFunc(int index){ this->index = index; } void myFunc(int index){ m_index = index; }
我用它来区分这两者。 另外,当我打包呼叫,如从Windows DLL,从DLL的RecvPacket(…)可能会包裹在我的代码中的RecvPacket(…) 。 在这些特定的场合,使用像“_”这样的前缀可能会使两者看起来相似,容易识别哪个是哪个,但是编译器不同
成员前缀的主要原因是区分成员函数local和成员variables名称相同。 如果你使用带有名字的getter,这是很有用的。
考虑:
class person { public: person(const std::string& full_name) : full_name_(full_name) {} const std::string& full_name() const { return full_name_; } private: std::string full_name_; };
在这种情况下,成员variables不能被称为full_name。 您需要将成员函数重命名为get_full_name()或以某种方式修饰成员variables。
那些公约就是这样。 大多数商店使用代码约定来简化代码的可读性,这样任何人都可以轻松查看一段代码,并快速解密诸如公共和私人成员之类的东西。
一些响应集中于重构,而不是命名约定,作为提高可读性的方式。 我不觉得可以取代另一个。
我知道程序员使用本地声明是不舒服的; 他们宁愿把所有的声明放在块的顶部(如C),所以他们知道在哪里find它们。 我发现,在范围允许的情况下,在第一次使用variables的地方声明variables减less了我花费时间向后查找声明的时间。 (即使是小函数也是如此)。这使我更容易理解我正在查看的代码。
我希望已经清楚这与成员命名惯例有何关系:当成员统一地加上前缀时,我根本不必回头看; 我知道该声明甚至不会在源文件中find。
我敢肯定,我并没有开始喜欢这些风格。 然而,随着时间的推移,在一贯使用的环境中工作,我优化了我的想法,以利用它们。 我认为很多现在感觉不舒服的人也会因为一贯的使用而更喜欢他们。
其他人试图在使用成员variables时强制使用this-> member
这通常是因为没有前缀 。 编译器需要足够的信息来parsing所讨论的variables,无论是前缀还是通过this
关键字都是唯一的名称。
所以,是的,我认为前缀仍然有用。 举个例子,我宁愿input'_'来访问一个成员,而不是'this->'。
其他语言将使用编码约定,他们只是倾向于不同。 例如,C#可能有两种不同的风格,人们倾向于使用C ++方法(_variable,mVariable或其他前缀,如匈牙利表示法),或者我称之为StyleCop方法。
private int privateMember; public int PublicMember; public int Function(int parameter) { // StyleCop enforces using this. for class members. this.privateMember = parameter; }
最后,这变成了人们所知道的,看起来最好的。 我个人认为,如果没有匈牙利符号,代码更具可读性,但是,如果匈牙利符号被附加,则可以更容易地find具有智能感知的variables。
在我上面的例子中,你不需要一个m前缀的成员variables,因为用这个前缀你的用法。 在编译器执行的方法中表示同样的事情。
这并不一定意味着其他方法是不好的,人们坚持什么工作。
当你有一个很大的方法或代码块时,立即知道是否使用局部variables或成员是很方便的。 这是为了避免错误和更清晰!
海事组织,这是个人的。 我没有放任何前缀。 无论如何,如果代码是公开的,我认为它应该有一些前缀,所以它可以更具可读性。
通常大公司正在使用它自己的所谓的“开发者规则”。
顺便说一下,我看到的最有趣也最聪明的就是DRY KISS(不要重复自己,保持简单,愚蠢)。 🙂
正如其他人已经说过,重要的是要口语化(适应命名风格和惯例,你正在写的代码库),并保持一致。
多年来,我一直在使用“this->”约定以及使用后缀下划线符号表示成员variables的大型代码库。 多年来,我也一直在小型项目上工作,其中一些项目没有任何命名成员variables的约定,另一些约定了成员variables命名的不同约定。 在那些较小的项目中,我一直发现那些缺乏规范的人是最难以快速理解的。
关于命名我非常肛门保留。 我会痛苦地把这个名字归咎于一个类或一个variables,如果我不能提出一个我认为是“好”的东西,我会select一些无意义的东西,并提供一个评论来描述它的真正含义是。 这样,至less这个名字的意思就是我的意思 – 只不过是一点点而已。 And often, after using it for a little while, I discover what the name should really be and can go back and modify or refactor appropriately.
One last point on the topic of an IDE doing the work–that's all nice and good, but IDEs are often not available in environments where I have perform the most urgent work. Sometimes the only thing available at that point is a copy of 'vi'. Also, I've seen many cases where IDE code completion has propagated stupidity such as incorrect spelling in names. Thus, I prefer to not have to rely on an IDE crutch.
The original idea for prefixes on C++ member variables was to store additional type information that the compiler didn't know about. So for example, you could have a string that's a fixed length of chars, and another that's variable and terminated by a '\0'. To the compiler they're both char *
, but if you try to copy from one to the other you get in huge trouble. So, off the top of my head,
char *aszFred = "Hi I'm a null-terminated string";
char *arrWilma = {'O', 'o', 'p', 's'};
where "asz" means this variable is "ascii string (zero-terminated) and "arr" means this variable is a character array.
Then the magic happens. The compiler will be perfectly happy with this statement:
strcpy(arrWilma, aszFred);
But you, as a human, can look at it and say "hey, those variables aren't really the same type, I can't do that".
Unfortunately a lot places use standards such as "m_" for member variables, "i" for integers no matter how used, "cp" for char pointers. In other words they're duplicating what the compiler knows, and making the code hard to read at the same time. I believe this pernicious practice should be outlawed by statute and subject to harsh penalties.
Finally, there's two points I should mention:
- Judicious use of C++ features allows the compiler to know the information you had to encode in raw C-style variables. You can make classes that will only allow valid operations. This should be done as much as practical.
- If your code blocks are so long that you forget what type a variable is before you use it, they are way too long. Don't use names, re-organize.
Our project has always used "its" as a prefix for member data, and "the" as a prefix for parameters, with no prefix for locals. It's a little cutesy, but it was adopted by the early developers of our system because they saw it used as a convention by some commercial source libraries we were using at the time (either XVT or RogueWave – maybe both). So you'd get something like this:
void MyClass::SetName(const RWCString &theName) { itsName = theName; }
The big reason I see for scoping prefixes (and no others – I hate Hungarian notation) is that it prevents you from getting into trouble by writing code where you think you're referring to one variable, but you're really referring to another variable with the same name defined in the local scope. It also avoids the problem of coming up with a variable names to represent that same concept, but with different scopes, like the example above. In that case, you would have to come up with some prefix or different name for the parameter "theName" anyway – why not make a consistent rule that applies everywhere.
Just using this-> isn't really good enough – we're not as interested in reducing ambiguity as we are in reducing coding errors, and masking names with locally scoped identifiers can be a pain. Granted, some compilers may have the option to raise warnings for cases where you've masked the name in a larger scope, but those warnings may become a nuisance if you're working with a large set of third party libraries that happen to have chosen names for unused variables that occasionally collide with your own.
As for the its/the itself – I honestly find it easier to type than underscores (as a touch typist, I avoid underscores whenever possible – too much stretching off the home rows), and I find it more readable than a mysterious underscore.
I think that, if you need prefixes to distinguish class members from member function parameters and local variables, either the function is too big or the variables are badly named. If it doesn't fit on the screen so you can easily see what is what, refactor.
Given that they often are declared far from where they are used, I find that naming conventions for global constants (and global variables, although IMO there's rarely ever a need to use those) make sense. But otherwise, I don't see much need.
That said, I used to put an underscore at the end of all private class members. Since all my data is private, this implies members have a trailing underscore. I usually don't do this anymore in new code bases, but since, as a programmer, you mostly work with old code, I still do this a lot. I'm not sure whether my tolerance for this habit comes from the fact that I used to do this always and am still doing it regularly or whether it really makes more sense than the marking of member variables.
It is useful to differentiate between member variables and local variables due to memory management. Broadly speaking, heap-allocated member variables should be destroyed in the destructor, while heap-allocated local variables should be destroyed within that scope. Applying a naming convention to member variables facilitates correct memory management.
I use it because VC++'s Intellisense can't tell when to show private members when accessing out of the class. The only indication is a little "lock" symbol on the field icon in the Intellisense list. It just makes it easier to identify private members(fields) easier. Also a habit from C# to be honest.
class Person { std::string m_Name; public: std::string Name() { return m_Name; } void SetName(std::string name) { m_Name = name; } }; int main() { Person *p = new Person(); p->Name(); // valid p->m_Name; // invalid, compiler throws error. but intellisense doesn't know this.. return 1; }
Code Complete recommends m_varname for member variables.
While I've never thought the m_ notation useful, I would give McConnell's opinion weight in building a standard.
I almost never use prefixes in front of my variable names. If you're using a decent enough IDE you should be able to refactor and find references easily. I use very clear names and am not afraid of having long variable names. I've never had trouble with scope either with this philosophy.
The only time I use a prefix would be on the signature line. I'll prefix parameters to a method with _ so I can program defensively around them.
In python leading double underscores are used to emulate private members. For more details see this answer
You should never need such a prefix. If such a prefix offers you any advantage, your coding style in general needs fixing, and it's not the prefix that's keeping your code from being clear. Typical bad variable names include "other" or "2". You do not fix that with requiring it to be mOther, you fix it by getting the developer to think about what that variable is doing there in the context of that function. Perhaps he meant remoteSide, or newValue, or secondTestListener or something in that scope.
It's an effective anachronism that's still propagated too far. Stop prefixing your variables and give them proper names whose clarity reflects how long they're used. Up to 5 lines you could call it "i" without confusion; beyond 50 lines you need a pretty long name.
I like variable names to give only a meaning to the values they contain, and leave how they are declared/implemented out of the name. I want to know what the value means, period. Maybe I've done more than an average amount of refactoring, but I find that embedding how something is implemented in the name makes refactoring more tedious than it needs to be. Prefixes indicating where or how object members are declared are implementation specific.
color = Red;
Most of the time, I don't care if Red is an enum, a struct, or whatever, and if the function is so large that I can't remember if color was declared locally or is a member, it's probably time to break the function into smaller logical units.
If your cyclomatic complexity is so great that you can't keep track of what is going on in the code without implementation-specific clues embedded in the names of things, most likely you need to reduce the complexity of your function/method.
Mostly, I only use 'this' in constructors and initializers.
I use m_ for member variables just to take advantage of Intellisense and related IDE-functionality. When I'm coding the implementation of a class I can type m_ and see the combobox with all m_ members grouped together.
But I could live without m_ 's without problem, of course. It's just my style of work.