C#、TS和Dart对比3:编译时常量和运行时常量
一、常量的一些概念
1、变量初始化或赋值后,值还可以改变,但常量一旦初始化或赋值后,值不可变。
2、有一些常量,必须在定义常量的时候初始化赋值,值在编译期决定,称之为编译时常量。C#、TS和Dart中,都使用const来定义,但使用略有区别,详见下文。
3、而有一些常量,即可以在编译期初始化赋值,也可以先定义,然后在运行时再赋值,称之为运行时常量。C#和TS中,使用readonly关键词来定义,且只能在类中使用;而Dart使用final关键词来定义,即可以在类中使用,也可以在方法中使用。
4、补充:Dart是为flutter而生的,而flutter中,const和final使用非常广泛,所以需要特别掌握。
三、编译时常量
1、C#中的const:即可以在方法中使用,也可以在类中使用。在类中使用时,具有静态字段特征(但不需要用static关键词修饰),可以通过【类名.常量名】的方式访问。
public class Program { public const int age = 18; //定义类中的实例常量字段,是类的数据成员。具有static静态成员的特征。 static void Main(string[] args) { //const int a; //报错,const定义编译时常量,必须初始化赋值 const int a = 1; //定义方法中的本地常量 //a = 2; //报错,常量赋值后不能再改变 Console.WriteLine(age); //在方法中使用类的常量字段 Console.WriteLine(a); //1 } }
2、TS中的const:只能在方法中使用,不能在类中使用
class Program{ //public const a2:number = 1; //报错,不能在类中使用const创建数据成员 main():number{ const a2:number = 2; //定义本地常量 return a1+a2; } } //const a1:number; //报错,const定义常量(此处为全局常量),必须初始化赋值 const a1:number = 1; //定义全局常量 let p1 = new Program(); console.log(a1); //1 console.log(p1.main()); //3
3、Dart中的const:即可以在方法中使用,也可以在类中使用。在类中使用时,不能使用const创建实例常量字段,必须定义为静态常量字段【static const...】。在Flutter中,const实例化widget(常量构造方法)的使用非常广泛,能够有效提升性能。
void main() { //const int a; a = 1; //报错,const创建常量时,必须初始化赋值 const int a1 = 1; const a2 = 2; //使用const定义常量,具有类型推荐功能,可以省略类型声明,但不能和var一起使用 const a3 = 2*a2; //可以使用其它常量为const常量赋值 /* * 以下为难点,const不仅可以定义常量,还可以用于创建常量值 * 使用const创建常量值,主要用于创建常量构造函数。常量构造函数创建的对象,不可以改变 */ var a4 = const []; //const创建了一个空数组常量值,并赋值给变量a4 a4 = [1]; //变量a4仍然可以改变,不能变的是使用【const []】创建的常量值 const a5 = const []; //表达式一定是常量的上下文中,可以消除冗余,等效于const a5 = []; //a5 = [1]; //报错,a5是常量,不能更改 print(a1); //1 print(a2); //2 print(a3); //4 print(a4); //[1] print(Employee.age); //18。直接通过类调用静态常量字段 } class Employee{ //const age = 18; //报错,const只可以创建静态常量字段 static const age = 18; }
四、运行时常量
1、C#中的readonly:在类中定义静态常量字段和实例常量字段,在运行时设置值
public class Program { public const int age = 18; //定义类中的常量字段,是类的数据成员 static void Main(string[] args) { var s1 = new Shape(1.0,2.0); //s1.NumberOfSides = 5; //报错,实例常量无法修改 } } public class Shape { public static readonly string Color = "red"; //readonly常量可以定义为静态字段,声明时可以初始化 public readonly int NumberOfSides; //readonly常量也可以定义为实例字段,且可以是运行时常量,声明时不初始化 //readonly常量NumberOfSides的值,在运行时决定,可以根据不同的构造函数设置不同的值 public Shape(double side1,double side2) { this.NumberOfSides = 4; Console.WriteLine("构造一个矩形"); } public Shape(double side1,double side2,double side3) { this.NumberOfSides = 3; Console.WriteLine("构造一个三角形"); } public void SetNumberOfSides() { //NumberOfSides = 5; //报错,类的普通函数中无法修改readonly常量 } }
2、TS中的readonly:用法和C#一样,和上例不同的是,TS不支持构造函数重载,只能有一个constrator
//模仿入口函数,创建一个立即执行函数【(()={})()】 (()=>{ class Shape { public static readonly Color:string = "red"; public readonly NumberOfSides:number; constructor(side1:number,side2:number){ this.NumberOfSides = 4; console.log("构造一个矩形"); } public setNumberOfSide():void{ //this.NumberOfSides = 5; //报错,类的普通函数中无法修改readonly常量 } } var s1 = new Shape(1.0,2.0); //s1.NumberOfSides = 5; //报错,实例常量无法修改 })()
3、Dart中的final:即可以在方法中使用,也可以在类中使用。 Flutter中,在定义组件属性时,final使用非常广泛。
void main(List<String> args) { //final可以和const一样使用,可以省略类型关键词(但不能和var一起使用) final int a1 = 1; final a2 = 2; //a1 = 3; //报错,常量一旦赋值,不能在修改 //final常量,在运行时才赋值,可以先声明,再赋值 //如果此处声明为【final a3;】,则类型推断为dynamic final int a3; a3 = 3; //体现final是运行时常量更好的例子 //const int a4 = getNum(); //报错,const不能用于运行时 final int a4 = getNum();
final a5 = DateTime.now(); } int getNum() { return 4; }
//类中使用final声明常量字段。注:如果使用了常量构造函数,所有字段必须使用final声明 class Shape { static final String color = "red"; //静态常量,需要声明时初始化 final int numberOfSides; //声明实例常量字段,可以不初始化 Shape(this.numberOfSides); //使用构造函数,当创建实例时,设置final常量的值 }