2.1C#的程序结构和组成;2.2C#的数据类型和变量
bigegpt 2025-01-05 15:50 13 浏览
C#是一种面向对象的编程语言,它具有简洁、优雅和强大的特性。C#可以用来开发各种应用程序,如桌面应用、网页应用、移动应用、游戏等。本文将介绍C#的基本语法,包括程序结构和组成,以及数据类型和变量的使用。
2.1 C#的程序结构和组成
一个典型的C#程序由以下几个部分组成:
- 使用using关键字引入的库,库是一组预定义的类和方法,可以提供各种功能,如输入输出、数学运算、字符串处理等。C#程序可以有多个using语句。
- 使用namespace关键字声明的命名空间,命名空间是一种组织类的方式,可以避免类名冲突,也可以提高代码的可读性。C#程序可以有多个命名空间,也可以嵌套使用。
- 使用class关键字声明的类,类是一种封装数据和行为的结构,是面向对象编程的基本单位。C#程序可以有多个类,也可以继承或实现其他类或接口。
- 类中的成员和属性,包括字段、属性、方法、构造函数、析构函数、事件、索引器等。这些成员和属性可以定义类的状态和行为,也可以通过访问修饰符控制其可见性和访问权限。
- 程序的入口点,即Main方法,它是一个静态方法,用于启动程序的执行。C#程序必须有一个且只有一个Main方法,它可以接受一个字符串数组作为参数,也可以没有参数。在C# 9中,还可以使用顶层语句作为程序的入口点1。
- 其他语句和表达式,用于实现程序的逻辑和功能,如变量声明、赋值、运算、条件判断、循环、异常处理等。
- 注释,用于说明代码的作用和意义,不会被编译器执行。C#中有两种注释方式:单行注释(以//开头)和多行注释(以/开头,以/结尾)。
下面是一个简单的C#程序的示例2:
// 一个简单的C#程序
using System; // 引入System库
namespace printHelloCsharp // 声明命名空间
{
class HelloCsharp // 声明类
{
static void Main(string[] args) // 声明Main方法
{
/* 在控制台输出一行文字 */
Console.WriteLine("Hello C#.");
Console.ReadKey(); // 等待用户按键
}
}
}
2.2 C#的数据类型和变量
数据类型是指在程序中使用的数据的种类和特征。C#中有两大类数据类型:值类型和引用类型。
值类型是指直接存储数据本身的类型,如整数、浮点数、字符、布尔值等。值类型在内存中占用固定大小的空间,并在栈上分配。值类型之间赋值时会复制数据本身。
引用类型是指存储数据引用(地址)的类型,如对象、数组、字符串等。引用类型在内存中占用不固定大小的空间,并在堆上分配。引用类型之间赋值时会复制引用(地址),而不是数据本身。
变量是指在程序中用来存储和操作数据的标识符,它有一个名称和一个类型。变量的名称必须遵循C#的命名规则,不能与关键字或其他变量重名。变量的类型决定了变量可以存储的数据范围和精度,以及可以对变量进行的操作。
在C#中,声明变量的一般语法是:
type variableName = value;
其中,type是变量的数据类型,variableName是变量的名称,value是变量的初始值。如果不指定初始值,那么变量会被赋予默认值( §9.3)。
例如:
int x = 10; // 声明一个整数类型的变量x,并赋值为10
double y; // 声明一个双精度浮点类型的变量y,没有赋值
char z = 'A'; // 声明一个字符类型的变量z,并赋值为'A'
在C#中,还可以一次声明多个同类型的变量,用逗号分隔:
int a = 1, b = 2, c = 3; // 声明三个整数类型的变量a,b,c,并分别赋值
string name, address, phone; // 声明三个字符串类型的变量name,address,phone,没有赋值
在C#中,还可以使用var关键字来声明隐式类型的变量,即让编译器根据初始值来推断变量的类型。使用var关键字时,必须指定初始值,否则会编译错误。
例如:
var x = 10; // x的类型被推断为int
var y = 3.14; // y的类型被推断为double
var z = "Hello"; // z的类型被推断为string
在C#中,还可以使用dynamic关键字来声明动态类型的变量,即让编译器在运行时才确定变量的类型。使用dynamic关键字时,可以不指定初始值,也可以在程序中改变变量的类型。
例如:
dynamic x; // x没有初始值,也没有确定的类型
x = 10; // x现在是int类型
x = "Hello"; // x现在是string类型
x = true; // x现在是bool类型
需要注意的是,使用dynamic关键字会降低程序的性能和安全性,因为编译器无法检查动态类型变量的正确性。因此,在不确定变量类型的情况下,应该优先使用var而不是dynamic。
C#的值类型和引用类型
在C#中,数据类型可以分为两大类:值类型和引用类型12。
值类型是指直接存储数据本身的类型,如整数、浮点数、字符、布尔值等。值类型在内存中占用固定大小的空间,并在栈上分配。值类型之间赋值时会复制数据本身。
引用类型是指存储数据引用(地址)的类型,如对象、数组、字符串等。引用类型在内存中占用不固定大小的空间,并在堆上分配。引用类型之间赋值时会复制引用(地址),而不是数据本身。
下面是一个演示值类型和引用类型区别的示例1:
using System;
public struct MutablePoint // 结构体是值类型
{
public int X;
public int Y;
public MutablePoint(int x, int y) => (X, Y) = (x, y);
public override string ToString() => #34;({X}, {Y})";
}
public class Program
{
public static void Main()
{
var p1 = new MutablePoint(1, 2); // 声明一个结构体变量p1,并赋值为(1, 2)
var p2 = p1; // 声明一个结构体变量p2,并赋值为p1的副本
p2.Y = 200; // 修改p2的Y属性为200
Console.WriteLine(#34;{nameof(p1)} after {nameof(p2)} is modified: {p1}"); // 输出p1的值,不受p2的影响
Console.WriteLine(#34;{nameof(p2)}: {p2}"); // 输出p2的值,为(1, 200)
}
}
// 输出结果:
// p1 after p2 is modified: (1, 2)
// p2: (1, 200)
在上面的示例中,我们可以看到,当我们修改了p2的值时,p1的值并没有改变,这是因为p1和p2是两个独立的变量,它们存储在不同的内存地址上,它们之间只是复制了数据本身。
下面是另一个演示值类型和引用类型区别的示例2:
using System;
using System.Collections.Generic;
public struct TaggedInteger // 结构体是值类型
{
public int Number;
private List<string> tags; // 列表是引用类型
public TaggedInteger(int n)
{
Number = n;
tags = new List<string>();
}
public void AddTag(string tag) => tags.Add(tag);
public override string ToString() => #34;{Number} [{string.Join(", ", tags)}]";
}
public class Program
{
public static void Main()
{
var n1 = new TaggedInteger(0); // 声明一个结构体变量n1,并赋值为0
n1.AddTag("A"); // 给n1添加一个标签"A"
Console.WriteLine(n1); // 输出n1的值,为0 [A]
var n2 = n1; // 声明一个结构体变量n2,并赋值为n1的副本
n2.Number = 7; // 修改n2的Number属性为7
n2.AddTag("B"); // 给n2添加一个标签"B"
Console.WriteLine(n1); // 输出n1的值,为0 [A, B]
Console.WriteLine(n2); // 输出n2的值,为7 [A, B]
}
}
// 输出结果:
// 0 [A]
// 0 [A, B]
// 7 [A, B]
在上面的示例中,我们可以看到,当我们修改了n2的Number属性时,n1的值时,n1的Number属性也没有改变,这是因为n1和n2是两个独立的变量,它们存储在不同的内存地址上,它们之间只是复制了数据本身。但是,当我们给n2添加了一个标签"B"时,n1的标签列表也发生了变化,这是因为n1和n2共享了同一个引用类型的数据成员tags,它们指向了同一个列表对象,它们之间只是复制了引用(地址)。
C#的装箱和拆箱
在C#中,有时我们需要将值类型转换为引用类型,或者将引用类型转换为值类型。这种转换过程称为装箱和拆箱 。
装箱是指将值类型转换为引用类型的过程。装箱是隐式的,即不需要显式地进行类型转换。
例如:
int i = 10; // 声明一个整数类型的变量i,并赋值为10
object o = i; // 声明一个对象类型的变量o,并赋值为i,此时发生了装箱
在上面的示例中,我们将整数类型的变量i赋值给了对象类型的变量o。由于对象类型是一个引用类型,它必须存储一个地址而不是一个值。因此,CLR(公共语言运行时)会在堆上创建一个新的System.Object对象,并将i的值包装在其中,然后将该对象的地址赋值给o。因此,这个过程就像把一个值装进一个盒子里一样,所以叫做装箱。
拆箱是指将引用类型转换为值类型的过程。拆箱是显式的,即需要显式地进行类型转换。
例如:
object o = 10; // 声明一个对象类型的变量o,并赋值为10,此时发生了装箱
int i = (int)o; // 声明一个整数类型的变量i,并赋值为o,此时发生了拆箱
在上面的示例中,我们将对象类型的变量o赋值给了整数类型的变量i。由于整数类型是一个值类型,它必须存储一个值而不是一个地址。因此,CLR(公共语言运行时)会从堆上找到o所指向的System.Object对象,并从中提取出i的值,然后赋值给i。因此,这个过程就像把一个盒子里的值拆出来一样,所以叫做拆箱。
需要注意的是,装箱和拆箱会影响程序的性能和内存消耗,因为它们涉及到堆上对象的创建和销毁。因此,在不必要的情况下,应该避免使用装箱和拆箱。
C#的常量和只读变量
在C#中,有时我们需要定义一些不可变的数据,即在程序中不能被修改的数据。这种数据可以使用常量或只读变量来表示。
常量是指在编译时就确定其值的变量,它必须在声明时就初始化,并且不能再次赋值。常量是隐式静态的,即可以通过类名.常量名的方式来访问。常量只能用于基本类型或字符串类型。
例如:
public class Math
{
public const double PI = 3.14; // 声明一个双精度浮点类型的常量PI,并赋值为3.14
public const string NAME = "Math"; // 声明一个字符串类型的常量NAME,并赋值为"Math"
}
Console.WriteLine(Math.PI); // 输出3.14
Console.WriteLine(Math.NAME); // 输出Math
只读变量是指在运行时才确定其值的变量,它可以在声明时或构造函数中初始化,并且不能再次赋值。只读变量不是隐式静态的,即需要通过对象实例来访问。只读变量可以用于任何类型。
例如:
public class Person
{
public readonly int ID; // 声明一个整数类型的只读变量ID
public readonly string Name; // 声明一个字符串类型的只读变量Name
public Person(int id, string name)
{
ID = id; // 在构造函数中初始化ID
Name = name; // 在构造函数中初始化Name
}
}
Person p = new Person(1, "Alice"); // 创建一个Person对象p,并传入ID和Name的值
Console.WriteLine(p.ID); // 输出1
Console.WriteLine(p.Name); // 输出Alice
需要注意的是,常量和只读变量之间有一些区别:
- 常量在编译时就确定其值,而只读变量在运行时才确定其值。这意味着常量可以用于属性、索引器、方法或构造函数的参数或返回值,而只读变量不能。
- 常量是隐式静态的,而只读变量不是。这意味着常量可以通过类名来访问,而只读变量需要通过对象实例来访问。
- 常量只能用于基本类型或字符串类型,而只读变量可以用于任何类型。这意味着常量不能用于自定义类型或数组类型,而只读变量可以。
- 常量在编译时就被嵌入到引用它们的代码中,而只读变量在运行时才被解析。这意味着如果常量的值发生了改变,那么引用它们的代码也需要重新编译才能获取新的值,而只读变量不需要。
- 常量必须在声明时就初始化,而只读变量可以在声明时或构造函数中初始化。这意味着常量不能使用动态计算的值来初始化,而只读变量可以。
因此,在确定数据不会改变的情况下,如果数据是基本类型或字符串类型,并且需要在编译时就确定其值,那么可以使用常量;否则,应该使用只读变量。
C#的枚举和结构
在C#中,有时我们需要定义一些特殊的数据类型,来表示一组有限的命名常量或一组相关的数据成员。这种数据类型可以使用枚举或结构来表示。
枚举是指由一组命名常量组成的值类型,它有一个基础的整数类型。要定义一个枚举类型,使用enum关键字,并指定枚举成员的名称:
enum Season // 声明一个枚举类型Season
{
Spring, // 声明一个枚举成员Spring
Summer, // 声明一个枚举成员Summer
Autumn, // 声明一个枚举成员Autumn
Winter // 声明一个枚举成员Winter
}
默认情况下,枚举成员的关联常量值是int类型的,它们从零开始,并按照声明的顺序递增。你也可以显式地指定任何其他整数类型作为枚举的基础类型。你也可以显式地指定枚举成员的关联常量值,如下例所示:
enum ErrorCode : ushort // 声明一个基础类型为ushort的枚举类型ErrorCode
{
None = 0, // 声明一个枚举成员None,并赋值为0
Unknown = 1, // 声明一个枚举成员Unknown,并赋值为1
ConnectionLost = 100, // 声明一个枚举成员ConnectionLost,并赋值为100
OutlierReading = 200 // 声明一个枚举成员OutlierReading,并赋值为200
}
你不能在枚举类型的定义中定义方法。要给枚举类型添加功能,可以创建扩展方法。
枚举类型的默认值是E是由表达式(E)0产生的值,即使零没有对应的枚举成员。
你可以使用枚举类型来表示一组互斥的选择或一组选择的组合。要表示选择的组合,可以将枚举类型定义为位标志。
如果你想让枚举类型表示选择的组合,可以将枚举成员定义为位字段,即枚举成员的关联值应该是2的幂。然后,你可以使用按位逻辑运算符|或&来组合选择或交集选择的组合,分别。要指示枚举类型声明了位字段,可以将Flags属性应用于它。如下例所示,你还可以在枚举类型的定义中包含一些典型的组合。
[Flags] // 应用Flags属性
public enum Days // 声明一个枚举类型Days
{
None = 0b_0000_0000, // 声明一个枚举成员None,并赋值为0
Monday = 0b_0000_0001, // 声明一个枚举成员Monday,并赋值为1
Tuesday = 0b_0000_0010, // 声明一个枚举成员Tuesday,并赋值为2
Wednesday = 0b_0000_0100, // 声明一个枚举成员Wednesday,并赋值为4
Thursday = 0b_0000_1000, // 声明一个枚举成员Thursday,并赋值为8
Friday = 0b_0001_0000, // 声明一个枚举成员Friday,并赋值为16
Saturday = 0b_0010_0000, // 声明一个枚举成员Saturday,并赋值为32
Sunday = 0b_0100_0000, // 声明一个枚举成员Sunday,并赋值为64
Weekend = Saturday | Sunday // 声明一个枚举成员Weekend,并赋值为Saturday和Sunday的组合
}
public class FlagsEnumExample
{
public static void Main()
{
Days meetingDays = Days.Monday | Days.Wednesday | Days.Friday; // 声明一个Days类型的变量meetingDays,并赋值为Monday,Wednesday和Friday的组合
Console.WriteLine(meetingDays); // 输出Monday, Wednesday, Friday
Days workingFromHomeDays = Days.Thursday | Days.Friday; // 声明一个Days类型的变量workingFromHomeDays,并赋值为Thursday和Friday的组合
Console.WriteLine(#34;Join a meeting by phone on {meetingDays & workingFromHomeDays}"); // 输出Join a meeting by phone on Friday
bool isMeetingOnTuesday = (meetingDays & Days.Tuesday) == Days.Tuesday; // 判断meetingDays是否包含Tuesday
Console.WriteLine(#34;Is there a meeting on Tuesday: {isMeetingOnTuesday}"); // 输出Is there a meeting on Tuesday: False
var a = (Days)37; // 将整数37转换为Days类型的变量a
Console.WriteLine(a); // 输出Monday, Wednesday, Saturday
}
}
在上面的示例中,我们可以看到,我们可以使用按位或运算符|来将多个枚举成员组合成一个变量,也可以使用按位与运算符&来判断一个变量是否包含某个枚举成员。我们还可以将一个整数转换为对应的枚举类型,或者将一个枚举类型转换为对应的整数。
结构是指可以封装数据和相关功能的值类型,它类似于类,但有一些区别。你使用struct关键字来定义一个结构类型:
public struct Coords // 声明一个结构类型Coords
{
public Coords(double x, double y) // 声明一个带参数的构造函数
{
X = x;
Y = y;
}
public double X { get; } // 声明一个只读属性X
public double Y { get; } // 声明一个只读属性Y
public override string ToString() => #34;({X}, {Y})"; // 重写ToString方法
}
结构类型具有值语义,即一个结构类型的变量直接包含了结构的实例。默认情况下,变量的值在赋值、传递参数或返回结果时会被复制。对于结构类型的变量,会复制结构的实例。更多信息,请参阅值类型。
通常,你使用结构类型来设计小型的数据中心类型,它们提供很少或没有行为。例如,.NET使用结构类型来表示一个数字(整数和实数)、一个布尔值、一个Unicode字符、一个时间实例。如果你关注的是类型的行为,可以考虑定义一个类。类类型具有引用语义,即一个类类型的变量包含了一个指向类型实例的引用,而不是实例本身。
因为结构类型具有值语义,我们建议你定义不可变的结构类型。
readonly struct
你可以使用readonly修饰符来声明一个结构类型是不可变的。一个readonly struct的所有数据成员必须是只读的,如下所示:
- 任何字段声明必须有readonly修饰符
- 任何属性,包括自动实现的属性,必须是只读的。在C# 9.0及更高版本中,一个属性可以有一个init访问器。
- 这保证了一个readonly struct的任何成员都不会修改结构的状态。这意味着除了构造函数之外的其他实例成员都是隐式只读的。
注意,在一个readonly struct中,一个可变引用类型的数据成员仍然可以改变自己的状态。例如,你不能替换一个List<T>实例,但是你可以向它添加新的元素。
下面的代码定义了一个带有init-only属性设置器的readonly struct,它在C# 9.0及更高版本中可用:
public readonly struct Coords // 声明一个readonly struct Coords
{
public Coords(double x, double y) // 声明一个带参数的构造函数
{
X = x;
Y = y;
}
public double X { get; init; } // 声明一个带有init访问器的只读属性X
public double Y { get; init; } // 声明一个带有init访问器的只读属性Y
public override string ToString() => #34;({X}, {Y})"; // 重写ToString方法
}
ref struct
你可以使用ref修饰符在声明一个结构类型时,表示该结构类型的实例是分配在栈上的,并且不能逃逸到托管堆上。为了保证这一点,编译器限制了ref struct类型的使用,如下所示:
- 一个ref struct不能作为数组的元素类型。
- 一个ref struct不能作为类或非ref struct的字段的声明类型。
- 一个ref struct不能实现接口。
- 一个ref struct不能被装箱到System.ValueType或System.Object。
- 一个ref struct不能作为类型参数。
- 一个ref struct变量不能被lambda表达式或局部函数捕获。
- 一个ref struct变量不能在异步方法中使用。但是,你可以在同步方法中使用ref struct变量,例如,在返回Task或Task<TResult>的方法中。
- 一个ref struct变量不能在迭代器中使用。
你可以定义一个可释放的ref struct。为了做到这一点,要确保一个ref struct符合可释放模式。也就是说,它有一个实例Dispose方法,该方法是可访问的、无参数的,并且有一个void返回类型。你可以使用using语句或声明来处理一个可释放的ref struct实例。
通常,当你需要一个类型,它也包含了ref struct类型的数据成员时,你才会定义一个ref struct类型:
public ref struct CustomRef // 声明一个ref struct CustomRef
{
public bool IsValid; // 声明一个布尔值字段IsValid
public Span<int> Inputs; // 声明一个Span<int>类型的字段Inputs
public Span<int> Outputs; // 声明一个Span<int>类型的字段Outputs
}
在.NET中,System.Span<T>和System.ReadOnlySpan<T>是ref struct的例子。
相关推荐
- 5分钟搭建公网https网页文件服务器,免费权威TLS证书
-
请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习前言本文主要讲解如何快速搭建一个https网页文件服务器,并免费申请权威机构颁发的tls证...
- nginx负载均衡配置(nginx负载均衡配置两个程序副本)
-
Nginx是什么没有听过Nginx?那么一定听过它的“同行”Apache吧!Nginx同Apache一样都是一种WEB服务器。基于REST架构风格,以统一资源描述符(UniformResources...
- 19《Nginx 入门教程》Nginx综合实践
-
今天我们将基于Nginx完成两个比较有用的场景,但是用到的Nginx的配置非常简单。内部Yum源搭建内部Pip源搭建1.实验环境ceph1centos7.6内网ip:172.16....
- Nginx性能调优与优化指南(nginx优化配置大全)
-
Nginx性能调优需要结合服务器硬件资源、业务场景和负载特征进行针对性优化。以下是一些关键优化方向和具体配置示例:一、Nginx配置优化1.进程与连接数优化nginxworker_process...
- C++后端开发必须彻底搞懂Nginx,从原理到实战(高级篇)
-
本文为Nginx实操高级篇。通过配置Nginx配置文件,实现正向代理、反向代理、负载均衡、Nginx缓存、动静分离和高可用Nginx6种功能,并对Nginx的原理作进一步的解析。当需...
- 【Nginx】史上最全的Nginx配置详解
-
Nginx服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里,http块又包括http全局块和server块。Nginx是非常重要的负载均衡中间件,被广泛应用于大型...
- 【Nginx】Nginx 4种常见配置实例(nginx基本配置与参数说明)
-
本文主要介绍nginx4种常见的配置实例。Nginx实现反向代理;Nginx实现负载均衡;Nginx实现动静分离;Nginx实现高可用集群;Nginx4种常见配置实例如下:一、Nginx反向代理配...
- 使用nginx+allure管理自动化测试报告
-
allure在自动化测试中经常用来生成漂亮的报告,但是网上及官网上给出的例子都仅仅是针对单个测试用例文件的形式介绍的,实际使用中,自动化测试往往需要包含不止一个产品或项目,本文介绍如何使用nginx+...
- nginx配置文件详解(nginx配置文件详解高清版)
-
Nginx是一个强大的免费开源的HTTP服务器和反向代理服务器。在Web开发项目中,nginx常用作为静态文件服务器处理静态文件,并负责将动态请求转发至应用服务器(如Django,Flask,et...
- SpringCloud Eureka-服务注册与发现
-
1.Eureka介绍1.1学习Eureka前的说明目前主流的服务注册&发现的组件是Nacos,但是Eureka作为老牌经典的服务注册&发现技术还是有必要学习一下,原因:(1)一些早期的分布式微服...
- 微服务 Spring Cloud 实战 Eureka+Gateway+Feign+Hystrix
-
前言我所在项目组刚接到一个微服务改造需求,技术选型为SpringCloud,具体需求是把部分项目使用SpringCloud技术进行重构。本篇文章中介绍了Eureka、Gateway、Fe...
- 深度剖析 Spring Cloud Eureka 底层实现原理
-
你作为一名互联网大厂后端技术开发人员,在构建分布式系统时,是不是常常为服务的注册与发现而头疼?你是否好奇,像SpringCloudEureka这样被广泛使用的组件,它的底层实现原理到底是怎样的...
- 热爱生活,喜欢折腾。(很热爱生活)
-
原文是stackoverflow的一则高票回答,原文链接可能之前也有人翻译过,但是刚好自己也有疑惑,所以搬运一下,个人水平有限所以可能翻译存在误差,欢迎指正(如侵删)。尽管classmethod和st...
- GDB调试的高级技巧(详细描述gdb调试程序的全过程)
-
GDB是我们平时调试c/c++程序的利器,查起复杂的bug问题,比打印大法要好得多,但是也不得不说,gdb在默认情况下用起来并不是很好用,最近学习到几个高级点的技巧,分享下:一美化打印先上个例子...
- Arduino 实例(二十三)Arduino 给Python 编译器发送信息
-
1首先Python需要安装Pyserial库,在命令提示符中输入pipintallpyserial若是遇到提示‘pip‘不是内部或外部命令,也不是可运行的程序或批处理文件,则需要设置环境变...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- skip-name-resolve (63)
- linuxlink (65)
- pythonwget (67)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)