Wiki

Clone wiki

qforce.bitbucket.org / 编码规范

所有语言

子项目命名

C#项目应该采用大写驼峰命名,例如CSharpBcpRpc。其他语言应该用小写字母和连字符命名,例如scala-bcp-rpcjson-stream。如果使用第三方库,应依照第三方库自己的命名建立目录结构,不必强制使用我们的命名规范,例如spine-csharp

全局变量

  • 不得使用全局变量或静态变量。
  • 可以使用全局函数或静态函数。
  • 可以使用初始化后就不再修改其内容的全局常量或静态常量。

final

默认情况下,每个class都应该标为final class@:final class

// Good!
final class MyScalaClass {
}

个别情况下,可以定义抽象类(即abstract class@:abstract class)。但每处定义抽象类时,必须增加注释,解释为什么没用final classinterfacetrait代替。

// Good!
abstract class BcpClient {
  // 本类为抽象类而不是trait,是为了禁止用户编写某个类同时从BcpServer.Session和BcpClient中派生
}

所有类都必须要么不可继承,要么就属于抽象类。禁止出现既允许继承又允许直接创建的类。

// Bad!
class NotFinalAndNotAbstract {
}

每个非抽象的publicprotected方法都必须标为final@:final。Scala的private[packageName]或Java的internal方法,也必须标为final

final class MyClass {
  // Good!
  final def foo():Unit = {
    println("Hello, World!");
  }
}
final class MyClass {
  // Bad!
  def foo():Unit = {
    println("Hello, World!");
  }
}

例外:常量属性

如果某个属性getter函数体是无副作用的常量,那么可以不加final或者标为virtual

// Good!
protected def heartBeatDelay:Duration = 3.seconds
var _heartBeatDelay:Duration = 3.secods

// Bad!
protected def heartBeatDelay:Duration = _heartBeatDelay // _heartBeatDelay受副作用影响,不是常量
// Good!
protected virtual int MaxConnectionsPerSession
{
  get { return 5; }
}

构造函数的副作用

严禁在一切抽象类和trait的构造函数中执行有副作用的操作1

其他类的构造函数一般也不应包含有副作用的操作。确有必要时,可以在final class的构造函数中执行有副作用的操作,但是必须写注释说明原因。

scala.App

此外,继承scala.App的用法也类似于在构造函数中执行副作用操作,因此应避免使用scala.App,而改用普通的def main(arguments: Array[String]):Unit创建入口类。

// Good!
object MyMain {

  def main(arguments: Array[String]):Unit = {
    // 副作用操作!
    doSomething()
  }

}
// Bad!
object MyMain extends App {

  // 副作用操作!
  doSomething()

}

注释的语言

开源项目中的代码应当用英文注释,其他代码应当用中文注释。

命名

函数、类和成员变量的命名应当使用完整英文单词,通常不得使用简写单词(abbreviations)。局部变量一般使用完整单词,但作用域非常短的局部变量,也可以使用单字母命名。例如:

// Good!
class AsynchronousInputStream {

  // Good!
  def available: Int

}
// Bad!
class AsyncInStr {

  // Bad!
  def avbl: Int

}

def swap(left: Array[Int], right: Array[Int]): Unit = {
  // Good!
  val t = left(0)
  left(0) = right(0)
  right(0) = t
}
不过,我们允许 http://sourceforge.net/adobe/flexsdk/wiki/Coding%20Conventions/#abbreviations 中列出的缩写惯例作为例外,这些例外可用于任何语言。

此外,可以使用常见的首字母缩写,见 http://sourceforge.net/adobe/flexsdk/wiki/Coding%20Conventions/#acronyms ,这些缩写可用于任何语言。

空代码块

必须在空代码块中写上注释或日志,说明为什么为空。例如:

// Good!
if (condition)
{
  // 因为某某原因,某某条件下不需要处理,直接忽略
}
else
{
  doSomething();
}
// Good!
xxxOption match {
  case Some(xxx) => {
    doSomething();
  }
  case None => {
    logger.fine("Skip xxx because of yyy")
  }
}
// Bad!
if (condition)
{
}
else
{
  doSomething();
}
// Bad!
xxxOption match {
  case Some(xxx) => {
    doSomething();
  }
  case None => {
  }
}

单个字母的小写驼峰和大写驼峰命名

如果某个变量以单个字母的单词开头,那么用于小写驼峰命名时,前两个字母都小写,用于大写驼峰命名时,前两个字母都大写,例如:

// Good!
emailAddress in CSharpConfiguration := "user@gmail.com"
// Bad!
eMailAddress in CsharpConfiguration := "user@gmail.com"

override

实现抽象方法时,不得省略override@Override修饰符:

final class AbstractBase extends Function1[Int, Int] {
  // Good!
  override final def apply(i:Int):Int = i
}
final class AbstractBase extends Function1[Int, Int] {
  // Bad!
  final def apply(i:Int):Int = i
}

循环依赖

禁止多个包相互依赖(依赖指在源代码中通过import或别的语句涉及其他包)。

package com.qifun.paraiso.foo

import com.qifun.paraiso.bar.Bar

class Foo
package com.qifun.paraiso.bar

import com.qifun.paraiso.baz.Baz

class Bar
package com.qifun.paraiso.baz

object Baz {

  // Bad!
  val foo = new com.qifun.paraiso.foo.Foo

}

class Baz {

  // Bad!
  import com.qifun.paraiso.foo.Foo

}

时间

绝对时间

在同一种语言内部,应当使用System.DateTimejava.util.Date等静态类型表示时间。

在需要跨平台传递时或者需要优化内存占用时,使用64位有符号整数表示绝对时间,数值是1970年1月1日以后的毫秒数。

相对时间

在同一种语言内部,应当使用scala.concurrent.duration.Duration等静态类型表示时间间隔。

在需要跨平台传递时或者需要优化内存占用时,应根据数值的取值范围选择使用IntLongFloatDouble表示相对时间。这种情况下,变量名必须加上秒、毫秒等时间单位,例如:

// Good!
var coolDownMilliseconds(default, null):Int;

// Good!
var timeOutSeconds(default, null):Float;
// Bad!
var coolDownTime(default, null):Int;

// Bad!
var timeOut(default, null):Float;

Scala

遵守Scala Style Guide,除非本文特别提及。

Scala IDE自动Format格式

当Scala IDE自动Format格式与 Scala Style Guide 发生冲突时,以Scala IDE自动Format格式为准。

包括且不限于以下情况:

主构造器参数换行时,使用2个空格缩进,而不是4个空格,例如:

// Good!
class Person(
  name: String,
  age: Int)
extends Entity {
}
// Bad!
class Person(
    name: String,
    age: Int)
  extends Entity {
  }

单行字数限制

参见 http://docs.scala-lang.org/style/indentation.html#line-wrapping 的换行规则。但单行宽度限制应改为120个半角字符,而不是80字符。

Continuation

得使用Continuation插件。

new

如果创建对象时,构造函数没有参数,那么应该省略括号。例如:

// Good!
throw new IllegalContentTypeException
// Bad!
throw new IllegalContentTypeException();

scala.Enumeration

不应使用scala.Enumeration,应当用sealed traitcase objectcase class代替。

object 中的 val

参见 http://stackoverflow.com/questions/9745488/naming-convention-for-scala-constants

使用大写驼峰命名,例如:

object Int {
  // Good!
  val MaxValue = 2147483647
}

不要使用小写驼峰:

object Int {
  // Bad!
  val maxValue = 2147483647
}

大写下划线是Java、ActionScript、Haxe的惯例,但并不是Scala的惯例,也不要使用:

object Int {
  // Bad!
  val MAX_VALUE = 2147483647
}

首字母缩写

在类名或变量名中如果出现首字母缩写,应该用驼峰命名法,例如:

// Good!
case class PublicId

// Good!
class JsonObject

// Good!
trait XmlEvent

// Good!
object UiElement

object Codec {
  // Good!
  val Utf8: Charset = ...

  // Good!
  def fromUtf8 (bytes: Array[Byte]): Array[Char] = ...
}

而不要像Flex编码规范那样全部字母大写:

// Bad!
case class PublicID

// Bad!
class JSONObject

// Bad!
trait XMLEvent

// Bad!
object UIElement

object Codec {
  // Bad!
  val UTF8: Charset = ...

  // Bad!
  def fromUTF8 (bytes: Array[Byte]): Array[Char] = ...
}

测试用例的命名

  • 测试用例的类名应该叫XxxTest,其中,Xxx是被测试的类名。
  • 测试方法的名称应该是完整的句子或者短语,不开源的测试用例可以用中文。

例如:

// Good
final class XxxTest {

  @Test
  final def `Xxx.foo should equals to Xxx.bar`():Unit = {
    assertEquals(Xxx.foo, Xxx.bar)
  }

}

导入Scala容器

  • 导入scala.collection.mutable包中的类型时,如果这个类型在scala.collection.immutable存在同名类型,那么需要改名以增加Mutable前缀。
  • 导入scala.collection.immutable包中的类型时不需要改名。
  • 除了Scala默认已经放入scala包的SeqIteratorIterable,尽量避免导入其他scala.collection中的类型。
// Good!
import scala.collection.immutable.SortedSet

// Good!
import scala.collection.mutable.{ SortedSet => MutableSortedSet }

// Good!
import scala.collection.mutable.LinkedList

// Bad!
import scala.collection.mutable.SortedSet

// Bad!
import scala.collection.SortedSet

// Bad!
import scala.collection.mutable.{ LinkedList => MutableLinkedList }

配置表

在Scala代码中涉及用haxe-import-csv导入的配置表的工作表名时,要么使用完全限定名,要么用import语句添加Hic前缀。相应的变量名,必须添加hic前缀。例如:

// Bad!
import com.qifun.paraiso.xlsx.Server
val server:Server = ???
// Good!
val hicServer:com.qifun.paraiso.xlsx.Server = ???
// Good!
import com.qifun.paraiso.xlsx.{ Server => HicServer }
val hicServer:HicServer = ???

zero-log配置文件

zero-log的配置文件应当命名为zero-log.config.scala。不用遵守http://docs.scala-lang.org/style/files.html

没有yield的for循环超过120个字符时应该改用多重嵌套

// Good!
for (x <- veryLongBoard.rows) {
  for (y <- veryLongBoard.files) {
    if (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongCondition) {
      printf("(%d, %d)", x, y)
    }
  }
}

// Bad!
for (x <- veryLongBoard.rows; y <- veryLongBoard.files if veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongCondition) {
  printf("(%d, %d)", x, y)
}

null

新编写的Scala原生代码一般不允许使用null,应该改用NoneOptionSome

// Good!
def getXxx():Option[String] = {
  if (condition) {
    Some(result)
  } else {
    None
  }
}
// Bad!
def getXxx():String = {
  if (condition) {
    result
  } else {
    null
  }
}

例外

以下情况可以使用null

  • Java标准库、Haxe生成的代码有时会返回null。在Scala中应当在调用这些API后立即检查返回值是否是null
  • 在容器中大量保存的类的成员变量,如果在性能关键的场合,可以使用null来节省内存。
class Foo {
  // 首选的写法
  val bar: AtomicReference[Option[String]] = new AtomicReference(None)
}
type FooSeq = Seq[Foo]
class Foo {
  // 为了优化性能时,可以接受的写法
  val bar: AtomicReference[String] = new AtomicReference(null)
}
type FooSeq = Seq[Foo]

return

一般情况下不得使用return关键字。

如果有迫不得已的原因要用return,必须写上行注释,解释为什么这么写。

// Bad!
if (condition) {
  foo()
  return
}
bar()
// Good!
if (condition) {
  foo()
} else {
  bar()
}

break

一般情况下不得使用scala.util.control.Breaks.break

如果有迫不得已的原因要用break,必须写上行注释,解释为什么这么写。

// Bad!
import scala.util.control.Breaks._
var seq = 10000 until 20000
breakable {
  for (i <- seq) {
    if (i % 7 != 0) {
      println(i)
    } else {
      break
    }
  }
}
// Good!
var seq = 10000 until 20000
for (i <- seq.view.takeWhile(_ % 7 != 0)) {
  println(i)
}

var

一般情况下不得使用var关键字。

如果有迫不得已的原因要用var,必须写上行注释,解释为什么这么写。

// Bad!
var i = 0
while (i < 10) {
  println(i)
  i += 1
}
// Good!
for (i <- 0 until 10) {
  println(i)
}

scala-stm

在私有函数中使用STM

类或包内部使用STM的privateinternal函数,如果可能被其他atomic块调用,那么应该增加(implicit txn:InTxn)参数:

object MyClass {

  private val intRef = Ref.make[Int]

  // Bad!
  private def foo(n: Int): Unit = {
    atomic { implicit txn =>
      intRef() = intRef() + n
    }
  }

  def main(arguments: Array[String]): Unit = {
    atomic { implicit txn =>
      foo(100)
    }
  }

}
object MyClass {

  private val intRef = Ref.make[Int]

  // Good!
  private def foo(n: Int)(implicit txn:InTxn): Unit = {
    intRef() = intRef() + n
  }

  def main(arguments: Array[String]): Unit = {
    atomic { implicit txn =>
      foo(100)
    }
  }

}

嵌套atomic

禁止在atomic块内嵌套atomic块:

// Bad!
atomic { implicit txn =>
  atomic { implicit txn =>
  }
}
// Bad!
def foo()(implicit txn: InTxn):Unit = {
  atomic { implicit txn =>
  }
}

atomic中的副作用操作

禁止在Scala STM的atomic块内执行有副作用的操作1

// Bad!
atomic { implicit txn =>
  socketRef().send(???)
}
// Good!
val socket = atomic { implicit txn =>
  socketRef()
}
socket.send(???)

atomic中的内部类

禁止在Scala STM的atomic块内以及atomic的钩子函数内定义内部类:

// Bad!
atomic { implicit txn =>
  Txn.afterCommit { _ =>
    // Bad!
    executor.schedule(new Runnable {
      override final def run() = ???
    })
  }
}
// Good!
class MyRunnable extends Runnable {
  override final def run() = ???
}
atomic { implicit txn =>
  Txn.afterCommit { _ => executor.schedule(new MyRunnable) }
}

atomic中的内部函数

一般禁止在Scala STM的atomic块内定义内部函数。

// Bad!
atomic { implicit txn =>
  future.onComplete { result =>
    ???
  }
}
// Bad!
atomic { implicit txn =>
  Txn.afterCommit { _ =>
    future.onComplete { result =>
      ???
    }
  }
}

有两种情况是例外,可以用内部函数:

用于Scala容器操作的回调函数
// Good!
atomic { implicit txn =>
  intRef() = seqRef().find { _ > 5 }
}
Txn钩子

允许创建不超过3行代码的内部函数,用于注册Txn.afterCommitTxn钩子。

// Good!
atomic { implicit txn =>
  Txn.afterCommit { _ => shutedDown() }
}

注意,如果创建超过3行代码的钩子,仍然必须定义在atomic之外

// Bad!
atomic { implicit txn =>
  Txn.afterCommit { _ => 
    shutedDown()
    socket.send(data1)
    socket.send(data2)
    socket.send(data3)
  }
}
// Good!
def onAfterCommit() = {
  shutedDown()
  socket.send(data1)
  socket.send(data2)
  socket.send(data3)
}
atomic { implicit txn =>
  Txn.afterCommit { _ => onAfterCommit() }
}

ActionScript 和 Haxe

除了本文特别提及之处,应遵守Flex 编码规范

单行长度

单行长度通常不得超过120字符。

包命名

使用com.qifun.项目.模块.子模块,其中如果出现连续的单词,用小写驼峰命名,例如:

package com.qifun.helloWorld.logging;

其中属于QForce引擎,但是不开源的部分,应当放在com.qifun.qforce.子项目包中。

首字母缩写

不要像Flex编码规范那样的全部字母大写,应当像普通单词一样用驼峰命名。

缩进

使用 2 个空格缩进而不是 4 个空格。

Haxe

注释

类型及其成员的注释应当用Dox语法。

如果某些代码只为生成文档专用,应当放在#if doc_gen中。例如:

#if doc_gen
/**
  定义了所有内置插件的模块。

  `using com.qifun.jsonStream.Plugins;`将启用`builderPlugin`包、`deserializerPlugin`包和`serializerPlugin`包中的所有插件。
**/
@:final
extern class Plugins{}
#end

预编译变量

应使用小写字母加下划线命名。例如:

# Good!
haxe -D avatar_shader_render
// Good!
#if avatar_shader_render
var shader = new Shader();
#end

return

一般情况下Haxe中return关键字写在函数声明的末尾,而不是在过程代码中。在函数返回值为Void时例外。

如果有迫不得已的原因要在过程代码中用return,必须写上行注释,解释为什么这么写。

// good!
function foo1():Int return
{
   var a:Int = 5;
   //do something
   a;
}

// good!
function foo2():Void
{
  //do something
}

// bad!
function foo3():Int
{
   var a:Int = 5;
   //do something
   return a;
}

enum

枚举类型用大写驼峰命名,枚举值用大写加下划线命名(类似常量)。

// Good!
enum DockDirection
{
  LEFT;
  RIGHT;
  TOP;
  BOTTOM;
  ABSOLUTE(x:Float, y:Float);
}

switch

遵守Flex编码规范。

  • 每一个case都必须省略break; 语句。
  • case之间也不必要额外的换行。
  • case后强制使用花括号。

例如:

// Good!
switch(event.type)
{
  case Event.COMPLETE, IOErrorEvent.IO_ERROR:
  {
    foo();
    bar();
  }
  case Event.INIT:
  {
    baz();
  }
  default:
  {
    throw "Unexpected event type";
  }
}

null

原则上不使用null

// Bad!
var foo:MyType = null;

如果某处代码一定要使用null,应该把可能为null的变量类型标记为Null<MyType>,例如:

// Good!
static function depth(state:Null<State<Dynamic>>):Int
{
  if (state != null)
  {
    return depth(state.master) + 1;
  }
  else
  {
    return 0;
  }
}

对于允许传null的函数参数,标记为可选参数亦可,例如:

// Good!
static function depth(?state:State<Dynamic>):Int
{
  if (state != null)
  {
    return depth(state.master) + 1;
  }
  else
  {
    return 0;
  }
}

Interface

Interface的命名通常是名词或形容词,且以I为开头。

在编写RPC相关接口时,如果接口内容为服务器端协议,则必须以-Service来结尾;如果接口内容为客户端协议,则必须以-Notification来结尾

// Interface for RPC

// Good!
interface ILoginService
{
}

// Good!
interface IMessageNotification
{
}

// Bad!
interface Login
{
}

哈希表

  • 键类型如果是字符串,应使用StringMap<MyValueType>而不是Dynamic<MyValueType>。
  • 键类型如果是整数,应使用IntMap<MyValueType>而不是Array<MyValueType>。
  • 键类型如果是引用,应使用ObjectMap<MyValueType>。
// Good!
var fooByString = new StringMap<Foo>();

// Bad!
var fooByString = new Dynamic<Foo>();

// Bad!
var fooByString:Dynamic<Foo> = {};

// Good!
var fooByID = new IntMap<Foo>();

// Bad!
var fooByID = new Array<Foo>();

// Bad!
var fooByID:Array<Foo> = [];

// Good
var idsOfFoo = new ObjectMap<Foo, Int>();

using

对于同一个公有静态函数,在定义这个静态函数的文件以外,每处调用这个静态函数的语法必须相同。即,要么每处都以using语法调用,要么每处都以普通语法调用。

如果某个公有静态函数的第一个参数名为self,那么必须以using语法调用这个静态函数。

例如:

class FooHelper
{
  // Good!
  public static function mustUsing(self:Foo):Void
  {
  }

}

using FooHelper;
class Main
{
  public static function main():Void
  {
    // Good!
    new Foo().mustUsing();

    // Bad!
    FooHelper.mustUsing(new Foo());
  }

}

@:noUsing

而如果一个公有静态函数有参数,且第一个参数名不是self,那么必须把这个公有静态函数标记为@:noUsing。例如:

class FooHelper
{
  // Good!
  @:noUsing
  public static function mustNotUsing(foo:Foo):Void
  {
  }

}
class FooHelper
{
  // Bad!
  public static function mustNotUsing(foo:Foo):Void
  {
  }

}

第三方库

标准库和第三方库中存在许多静态函数不符合本编码规范。这些静态函数的第一个参数名不是self,同时也没有被标记@:noUsing。当调用这些静态函数时,除非本编码规范中特别提及,否则不应该使用using语法。例如:

// Bad!
using Reflect;

class Main
{

  public static function main():Void
  {
    var foo:Dynamic = {};

    // Good!
    Reflect.setField(foo, "fieldName", "fieldValue");

    // Bad!
    foo.setField("fieldName", "fieldValue");
  }

}

LambdaStringTools

作为上一条的例外,必须以using语法调用标准库的Lambda中的所有函数,以及StringTools中除了isEof以外的其他所有函数。例如:

using StringTools;
using Lambda;
class Main
{
  public static function main():Void
  {
    // Good!
    [ 1, 2, 3 ].has(2);

    // Bad!
    Lambda.has([ 1, 2, 3 ], 2);

    // Good!
    "   foo".ltrim();

    // Bad!
    StringTools.ltrim("   foo");
  }

}

定义跨平台使用数据结构

haxe中所有跨平台使用的数据结构中不可以出现可变成员变量,一定需要可变成员变量需使用Stm的数据结构。

如果需要跨平台使用的数据结构的构造函数是空构造函数,则其成员变量都要赋初值。通常情况每一个成员都使用Stm的数据结构。

如果需要跨平台使用的数据结构的构造函数是非空构造函数,则模仿scala的case class,构造函数要给每一个成员变量赋初值,构造函数参数和成员变量一一对应,且命名一致。其成员变量是不可变的,通常不使用Stm的数据结构,但特殊情况允许使用,需要用注释注明使用原因。

// Good!
class User
{
  public function new(userName:String, password:String):Void 
  {
    this.userName = userName;
    this.password = password;
  }
  var userName(default, null):String;
  var password(default, null):String;
}

// Bad!
class User
{
  var userName:String;
  var password:String;
}

// Good!
class User
{
  var userName:StmRef<String> = StmRef.empty();
  var password:StmRef<String> = StmRef.empty();
}

常量

编译时常量

编译时常量应该使用inline var,常量名所有字母都大写,单词之间用下划线隔开,例如:

class Foo
{
  public inline var MY_INLINE_VAR = 5678;
}

初始化后就不修改的对象成员变量

某些成员变量只会初始化一次,初始化后就不会修改,这类变量在类中定义时应该使用(default, null)修饰符,例如:

class Foo
{
  public var myReadOnlyVar(default, null):Int;

  public function new(myReadOnlyVar:Int)
  {
    this.myReadOnlyVar = myReadOnlyVar;
  }
}

如果这个变量的初始值是常量,也不需要访问this,那么应该直接用=来初始化,例如:

class Foo
{

  public var myReadOnlyVar(default, null):Int = 123;

}

初始化后就不修改的静态成员变量

某些成员变量只会初始化一次,初始化后就不会修改,这类变量在类中应该使用(default, never)修饰符,变量名则是大写下划线风格,例如:

class Foo
{

  public var MY_READ_ONLY_VAR(default, never):Array<Int> = [ 1, 2, 3 ];

}

结构化类型中的只读变量

结构化类型中的只读变量应该使用(default, never)修饰符,例如:

typedef Foo =
{
  var myReadOnlyVar(default, never):Int;
}

@:dox(hide)

有的类、宏和运行时函数仅用于模块内部使用,但是因为某些原因,必须设为public才能编译通过。那么这些类、宏和运行时函数应该标记为@:dox(hide),代表不生成文档的内部功能,例如:

class MyMacroClass
{

  private static function internalRuntimeHelper(s:String):Void
  {
    // 为myMacro专用的内部函数。
  }

  macro public static function myMacro():haxe.macro.Expr return
  {
    macro MyMacroClass.internalRuntimeHelper("some arguments");
  }

}

class Main
{
  public static function main()
  {
    MyMacroClass.myMacro(); // 编译失败,无法访问internalRuntimeHelper
  }
}
class MyMacroClass
{

  @:dox(hide) // Good!
  public static function internalRuntimeHelper(s:String):Void
  {
    // 为myMacro专用的内部函数。
  }

  macro public static function myMacro():haxe.macro.Expr return
  {
    macro MyMacroClass.internalRuntimeHelper("some arguments");
  }

}

class Main
{
  public static function main()
  {
    MyMacroClass.myMacro(); // 编译成功
  }
}

注意:不得跨模块访问@:dox(hide)的类成员;不得跨包访问@:dox(hide)的类:

// myPackage1/MyClass1.hx
package myPackage1;
class MyClass1
{
  @:dox(hide)
  public static function hideFunction():Void
  {
  }
  public static function main():Void
  {
    // Good!
    hideFunction();
  }

}

// myPackage1/MyClass2.hx
package myPackage1;

@:dox(hide)
class MyClass2
{
  public static function main():Void
  {
    // Bad!
    MyClass1.hideFunction();
  }
}
// myPackage2/MyClass3.hx
package myPackage2;

// Bad!
import myPackage1.MyClass2;

class MyClass3
{
}

C#

除了本文特别提及之处,应遵守Framework Design GuidelinesAll-In-One Code Framework Coding Standards

TODO

All-In-One Code Framework Coding Standards禁止使用TODO注释。这条规则适用于示例代码,但不适用于我们的产品代码。我们的产品代码中可以出现TODO注释。


  1. 常见的有副作用的操作包括创建文件、删除文件、移动文件、IO读写、启动线程、启动定时器。 

Updated