c#及其开发环境简介
c#概述
c# 是一个现代的、通用的、面向对象的编程语言,它是由微软(microsoft)开发的,由 ecma 和 iso 核准认可的。
c# 是由 anders hejlsberg 和他的团队在 .net 框架开发期间开发的。
c# 是专为公共语言基础结构(cli)设计的。cli 由可执行代码和运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言。
下面列出了 c# 成为一种广泛应用的专业语言的原因:
- 现代的、通用的编程语言。
- 面向对象。
- 面向组件。
- 容易学习。
- 结构化语言。
- 它产生高效率的程序
- 它可以在多种计算机平台上编译。
- .net 框架的一部分。
c#的编程功能
虽然 c# 的构想十分接近于传统高级语言 c 和 c++,是一门面向对象的编程语言,但是它与 java 非常相似,有许多强大的编程功能,因此得到广大程序员的青睐。
下面列出 c# 一些重要的功能:
- 布尔条件(boolean conditions)
- 自动垃圾回收(automatic garbage collection)
- 标准库(standard library)
- 组件版本(assembly versioning)
- 属性(properties)和事件(events)
- 委托(delegates)和事件管理(events management)
- 易于使用的泛型(generics)
- 索引器(indexers)
- 条件编译(conditional compilation)
- 简单的多线程(multithreading)
- linq 和 lambda 表达式
- 集成 windows
c#与.net的关系
在这里,主要讨论创建 c# 编程所需的工具。前面已经提到 c# 是 .net 框架的一部分,且用于编写 .net 应用程序。因此,在讨论运行 c# 程序的可用工具之前,让我们先了解一下 c# 与 .net 框架之间的关系。
.net
net是微软公司下的一个开发平台,.net核心就是.net framwork(.net框架)是.net程序开发和运行的环境,在这个平台下可以用不同的语言进行开发,因为.net是跨语言的一个平台。语言可以是c#,f#,j#,vb.net等等。java和.net不同的一点是,java是跨平台的,不跨语言的。.net跨平台仅限于安卓系统和 ios 苹果系统。
.net 框架是一个创新的平台,能帮您编写出下面类型的应用程序:
- windows 应用程序
- web 应用程序
- web 服务
.net 框架应用程序是多平台的应用程序。框架的设计方式使它适用于下列各种语言:c#、c++、visual basic、jscript、cobol 等等。所有这些语言可以访问框架,彼此之间也可以互相交互。
.net 框架由一个巨大的代码库组成,用于 c# 等客户端语言。下面列出一些 .net 框架的组件:
- 公共语言运行库(common language runtime - clr)
- .net 框架类库(.net framework class library)
- 公共语言规范(common language specification)
- 通用类型系统(common type system)
- 元数据(metadata)和组件(assemblies)
- windows 窗体(windows forms)
- asp.net 和 asp.net ajax
- ado.net
- windows 工作流基础(windows workflow foundation - wf)
- windows 显示基础(windows presentation foundation)
- windows 通信基础(windows communication foundation - wcf)
- linq
c#
c#是一个.net平台下的一个程序设计语言,仅仅是一个语言。是运行在.net clr上的,用于创建应用程序的高级语言。 程序设计语言是为了方便开发人员和计算机沟通的工具。
c#的集成开发环境
windows上编写c#程序
微软(microsoft)提供了下列用于 c# 编程的开发工具:
- visual studio 2010 (vs)
- visual c# 2010 express (vce)
- visual web developer
linux/mac os上编写c#程序
运行第一个helloworld程序
一个c#程序主要包含以下部分:
- 命名空间声明
- 一个class
- class方法
- class属性
- 一个main方法
- 语句&表达式
- 注释
c# 文件的后缀为 .cs。
以下创建一个 test.cs 文件,文件包含了可以打印出 "hello world" 的简单代码:
using system;
namespace helloworldapplication
{
class helloworld
{
static void main(string[] args)
{
/* 我的第一个 c# 程序*/
console.writeline("hello world");
console.readkey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
hello world
- 程序的第一行using system:-using关键字用于在程序中包含system命名空间。一个程序一般有多个using语句。
- 下一行是 namespace 声明。一个 namespace 里包含了一系列的类。helloworldapplication 命名空间包含了类 helloworld。
- 下一行是 class 声明。类 helloworld 包含了程序使用的数据和方法声明。类一般包含多个方法。方法定义了类的行为。在这里,helloworld 类只有一个 main 方法。
- 下一行定义了 main 方法,是所有 c# 程序的 入口点。main 方法说明当执行时 类将做什么动作。
- 下一行
/*...*/
将会被编译器忽略,且它会在程序中添加额外的注释。 - main 方法通过语句
console.writeline("hello world");
指定了它的行为。 - writeline 是一个定义在 system 命名空间中的 console 类的一个方法。该语句会在屏幕上显示消息
"hello world"
- 最后一行
console.readkey();
是针对 vs.net 用户的。这使得程序会等待一个按键的动作,防止程序从 visual studio .net 启动时屏幕会快速运行并关闭。
以下几点值得注意:
c# 是大小写敏感的。
- 所有的语句和表达式必须以分号(;)结尾。
- 程序的执行从 main 方法开始。
- 与 java 不同的是,文件名可以不同于类的名称。
c#基本语法
程序实例
c# 是一种面向对象的编程语言。在面向对象的程序设计方法中,程序由各种相互交互的对象组成。相同种类的对象通常具有相同的类型,或者说,是在相同的 class 中。
例如,以 rectangle(矩形)对象为例。它具有 length 和 width 属性。根据设计,它可能需要接受这些属性值、计算面积和显示细节。
让我们来看看一个 rectangle(矩形)类的实现,并借此讨论 c# 的基本语法:
using system;
namespace rectangleapplication
{
class rectangle
{
// 成员变量
int length;
int width;
public void acceptdetails()
{
length = 1;
width = 2;
}
public int getarea()
{
return length * width;
}
public void display()
{
console.writeline("length: {0}", length);
console.writeline("width: {0}", width);
console.writeline("area: {0}", getarea());
}
}
class executerectangle
{
static void main(string[] args)
{
rectangle r = new rectangle();
r.acceptdetails();
r.display();
console.readline();
}
}
}
运行结果:
c#基本语法
using关键字
在任何 c# 程序中的第一条语句都是:
using system;
一般在程序开头添加 using system;
,这时system.string
就可简写为string
。
例如:
// using system;
namespace consoleapp1
{
class program
{
static void main(string[] args)
{
system.string a = "hello world!";
system.console.writeline(a);
system.console.readkey();
}
}
}
和
using system;
namespace consoleapp1
{
class program
{
static void main(string[] args)
{
string a = "hello world!";
console.writeline(a);
console.readkey();
}
}
}
是等价的。using
关键字用于在程序中包含命名空间。一个程序可以包含多个 using
语句
class关键字
class关键字用于声明一个类
注释
注释是用于解释代码。编译器会忽略注释的条目。在 c# 程序中,多行注释以/*
开始,并以字符 */
终止,如下所示:
/* this program demonstrates
the basic syntax of c# programming
language */
单行注释是用 ‘//’ 符号表示。例如:
}//end class rectangle
成员变量
变量是类的属性或数据成员,用于存储数据。在上面的程序中,rectangle 类有两个成员变量,名为 length 和 width。
成员函数
函数是一系列执行指定任务的语句。类的成员函数是在类内声明的。我们举例的类 rectangle 包含了三个成员函数: acceptdetails、getarea 和 display。
实例化一个类
在上面的程序中,类 executerectangle 是一个包含 main() 方法和实例化 rectangle 类的类。
rectangle r = new rectangle();
标识符
标识符是用来识别类、变量、函数或任何其它用户定义的项目。在 c# 中,类的命名必须遵循如下基本规则:
- 标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
- 标识符中的第一个字符不能是数字。
- 标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ’ / \。
- 标识符不能是 c# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
- 标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
- 不能与c#的类库名称相同。
c#关键字
关键字是 c# 编译器预定义的保留字。这些关键字不能用作标识符,但是,如果您想使用这些关键字作为标识符,可以在关键字前面加上 @ 字符作为前缀。
在 c# 中,有些关键字在代码的上下文中有特殊的意义,如 get 和 set,这些被称为上下文关键字(contextual keywords)。
下表列出了 c# 中的保留关键字(reserved keywords)和上下文关键字(contextual keywords):
c#占位符{}
- 当 writeline() 函数有多个参数时,输出第一个参数(双引号内的)中的内容,而第二个及后面的参数中的内容替换掉第一个参数中对应位置的占位符一起输出。
static void main(string[] args)
{
console.writeline("a:{0},a:{1}",65,97);
console.readline();
}
运行结果:
- 如果第一个参数没有留占位符,那么第二个参数内容不输出
console.writeline("a:,a:",65,97);
运行结果
- 占位符从零开始计数,且占位符中的数字不能大于第二个及后面的参数的总个数减一(要求占位符必须有可替换的值)占位符数字与第二个及后面的参数字符位置一一对应。
static void main(string[] args)
{
console.writeline("a:{1},a:{0}",65,97);
console.readline();
}
c#数据类型
在c#中,变量分为以下几种类型:
- 值类型(value types)
- 引用类型(reference types)
- 指针类型(pointer types)
值类型
值类型变量可以直接分配给一个值。它们是从类 system.valuetype 中派生的。
值类型直接包含数据。比如 int、char、float,它们分别存储数字、字符、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。
如需得到一个类型或一个变量在特定平台上的准确尺寸,可以使用 sizeof 方法。表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。下面举例获取任何机器上 int 类型的存储尺寸:
using system;
namespace datatypeapplication
{
class program
{
static void main(string[] args)
{
console.writeline("size of int: {0}", sizeof(int));
console.readline();
}
}
}
引用类型
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的引用类型有:object、dynamic 和 string。
对象(object)类型
对象(object)类型 是 c# 通用类型系统(common type system - cts)中所有数据类型的终极基类。object 是 system.object 类的别名。所以对象(object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
object obj;
obj = 100; // 这是装箱
动态(dynamic)类型
您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
声明动态类型的语法:
dynamic <variable_name> = value;
例如:
dynamic d = 20;
字符串(string)类型
字符串(string)类型 允许您给变量分配任何字符串值。字符串(string)类型是 system.string 类的别名。它是从对象(object)类型派生的。字符串(string)类型的值可以通过两种形式进行分配:引号和 @引号。
例如:
string str = "runoob.com";
一个 @引号字符串:
@"runoob.com";
c# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
string str = @"c:\windows";
等价于:
string str = "c:\\windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"<script type=""text/javascript"">
<!--
-->
</script>";
用户自定义引用类型有:class、interface 或 delegate。我们将在以后的章节中讨论这些类型。
指针类型
指针类型变量存储另一个类型的内存地址,c#中的指针与c或c++中的指针有相同的功能,声明指针类型的语法:
type* identifier;
例如:
char* cptr;
int* iptr;
c#类型转换
类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 c# 中,类型铸造有两种形式:
- 隐式类型转换 - 这些转换是 c# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
- 显式类型转换 - 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。
下面的实例显示了一个显式的类型转换:
namespace typeconversionapplication
{
class explicitconversion
{
static void main(string[] args)
{
double d = 5673.74;
int i;
// 强制转换 double 为 int
i = (int)d;
console.writeline(i);
console.readkey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
5673
c#类型转换方法
c# 提供了下列内置的类型转换方法:
下面的实例把不同值的类型转换为字符串类型:
namespace typeconversionapplication
{
class stringconversion
{
static void main(string[] args)
{
int i = 75;
float f = 53.005f;
double d = 2345.7652;
bool b = true;
console.writeline(i.tostring());
console.writeline(f.tostring());
console.writeline(d.tostring());
console.writeline(b.tostring());
console.readkey();
}
}
当上面的代码被编译和执行时,它会产生下列结果:
75
53.005
2345.7652
true
c#常量
常量是固定值,程序执行期间不会改变。常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。
常量可以被当作常规的变量,只是它们的值在定义后不能被修改。
整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0x 表示十六进制,0 表示八进制,没有前缀则表示十进制。
整数常量也可以有后缀,可以是 u 和 l 的组合,其中,u 和 l 分别表示 unsigned 和 long。后缀可以是大写或者小写,多个后缀以任意顺序进行组合。
这里有一些整数常量的实例:
212 /* 合法 */
215u /* 合法 */
0xfeel /* 合法 */
078 /* 非法:8 不是一个八进制数字 */
032uu /* 非法:不能重复后缀 */
以下是各种类型的整数常量的实例:
85 /* 十进制 */
0213 /* 八进制 */
0x4b /* 十六进制 */
30 /* int */
30u /* 无符号 int */
30l /* long */
30ul /* 无符号 long */
浮点常量
一个浮点常量是由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。
这里有一些浮点常量的实例:
3.14159 /* 合法 */
314159e-5l /* 合法 */
510e /* 非法:不完全指数 */
210f /* 非法:没有小数或指数 */
.e55 /* 非法:缺少整数或小数 */
使用浮点形式表示时,必须包含小数点、指数或同时包含两者。使用指数形式表示时,必须包含整数部分、小数部分或同时包含两者。有符号的指数是用 e 或 e 表示的。
字符常量
字符常量是括在单引号里,例如,‘x’,且可存储在一个简单的字符类型变量中。一个字符常量可以是一个普通字符(例如 ‘x’)、一个转义序列(例如 ‘\t’)或者一个通用字符(例如 ‘\u02c0’)。
在 c# 中有一些特定的字符,当它们的前面带有反斜杠时有特殊的意义,可用于表示换行符(\n)或制表符 tab(\t)。在这里,列出一些转义序列码:
以下是一些转义序列字符的实例:
namespace escapechar
{
class program
{
static void main(string[] args)
{
console.writeline("hello\tworld\n\n");
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
hello world
字符串常量
字符串常量是括在双引号 "" 里,或者是括在 @"" 里。字符串常量包含的字符与字符常量相似,可以是:普通字符、转义序列和通用字符
使用字符串常量时,可以把一个很长的行拆成多个行,可以使用空格分隔各个部分。
这里是一些字符串常量的实例。下面所列的各种形式表示相同的字符串。
string a = "hello, world"; // hello, world
string b = @"hello, world"; // hello, world
string c = "hello \t world"; // hello world
string d = @"hello \t world"; // hello \t world
string e = "joe said \"hello\" to me"; // joe said "hello" to me
string f = @"joe said ""hello"" to me"; // joe said "hello" to me
string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt
string h = @"\\server\share\file.txt"; // \\server\share\file.txt
string i = "one\r\ntwo\r\nthree";
string j = @"one
two
three";
定义常量
常量是使用 const 关键字来定义的 。定义一个常量的语法如下:
const <data_type> <constant_name> = value;
下面的代码演示了如何在程序中定义和使用常量:
using system;
public class consttest
{
class sampleclass
{
public int x;
public int y;
public const int c1 = 5;
public const int c2 = c1 + 5;
public sampleclass(int p1, int p2)
{
x = p1;
y = p2;
}
}
static void main()
{
sampleclass mc = new sampleclass(11, 22);
console.writeline("x = {0}, y = {1}", mc.x, mc.y);
console.writeline("c1 = {0}, c2 = {1}",
sampleclass.c1, sampleclass.c2);
}
}
结果:
x = 11, y = 22
c1 = 5, c2 = 10
静态常量和动态常量
静态常量(编译时常量) const
在编译时就确定了值,必须在声明时就进行初始化且之后不能进行更改,可在类和方法中定义。定义方法如下:
const double a=3.14;// 正确声明常量的方法
const int b; // 错误,没有初始化
动态常量(运行时常量) readonly
在运行时确定值,只能在声明时或构造函数中初始化,只能在类中定义。定义方法如下:
class program
{
readonly int a=1; // 声明时初始化
readonly int b; // 构造函数中初始化
program()
{
b=2;
}
static void main()
{
}
}
静态常量与动态常量的使用场景
在下面两种情况下,可以使用 const 常量:
- 取值永久不变(比如圆周率、一天包含的小时数、地球的半径等)。
- 对程序性能要求非常苛刻。
除此之外的其他情况都应该优先采用 readonly 常量。
c#运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。c# 有丰富的内置运算符,分类如下:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他运算符
算法运算符
下表显示了 c# 支持的所有算术运算符。假设变量 a 的值为 10,变量 b 的值为 20,则:
实例:
请看下面的实例,了解 c# 中所有可用的算术运算符:
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
int a = 21;
int b = 10;
int c;
c = a + b;
console.writeline("line 1 - c 的值是 {0}", c);
c = a - b;
console.writeline("line 2 - c 的值是 {0}", c);
c = a * b;
console.writeline("line 3 - c 的值是 {0}", c);
c = a / b;
console.writeline("line 4 - c 的值是 {0}", c);
c = a % b;
console.writeline("line 5 - c 的值是 {0}", c);
// ++a 先进行自增运算再赋值
c = ++a;
console.writeline("line 6 - c 的值是 {0}", c);
// 此时 a 的值为 22
// --a 先进行自减运算再赋值
c = --a;
console.writeline("line 7 - c 的值是 {0}", c);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
line 1 - c 的值是 31
line 2 - c 的值是 11
line 3 - c 的值是 210
line 4 - c 的值是 2
line 5 - c 的值是 1
line 6 - c 的值是 22
line 7 - c 的值是 21
- c = a++: 先将 a 赋值给 c,再对 a 进行自增运算。
- c = ++a: 先将 a 进行自增运算,再将 a 赋值给 c 。
- c = a--: 先将 a 赋值给 c,再对 a 进行自减运算。
- c = --a: 先将 a 进行自减运算,再将 a 赋值给 c 。
实例
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
int a = 1;
int b;
// a++ 先赋值再进行自增运算
b = a++;
console.writeline("a = {0}", a);
console.writeline("b = {0}", b);
console.readline();
// ++a 先进行自增运算再赋值
a = 1; // 重新初始化 a
b = ++a;
console.writeline("a = {0}", a);
console.writeline("b = {0}", b);
console.readline();
// a-- 先赋值再进行自减运算
a = 1; // 重新初始化 a
b= a--;
console.writeline("a = {0}", a);
console.writeline("b = {0}", b);
console.readline();
// --a 先进行自减运算再赋值
a = 1; // 重新初始化 a
b= --a;
console.writeline("a = {0}", a);
console.writeline("b = {0}", b);
console.readline();
}
}
}
执行以上程序,输出结果为:
a = 2
b = 1
a = 2
b = 2
a = 0
b = 1
a = 0
b = 0
关系运算符
下表显示了 c# 支持的所有关系运算符。假设变量 a 的值为 10,变量 b 的值为 20,则:
实例
请看下面的实例,了解 c# 中所有可用的关系运算符:
using system;
class program
{
static void main(string[] args)
{
int a = 21;
int b = 10;
if (a == b)
{
console.writeline("line 1 - a 等于 b");
}
else
{
console.writeline("line 1 - a 不等于 b");
}
if (a < b)
{
console.writeline("line 2 - a 小于 b");
}
else
{
console.writeline("line 2 - a 不小于 b");
}
if (a > b)
{
console.writeline("line 3 - a 大于 b");
}
else
{
console.writeline("line 3 - a 不大于 b");
}
/* 改变 a 和 b 的值 */
a = 5;
b = 20;
if (a <= b)
{
console.writeline("line 4 - a 小于或等于 b");
}
if (b >= a)
{
console.writeline("line 5 - b 大于或等于 a");
}
}
}
结果
line 1 - a 不等于 b
line 2 - a 不小于 b
line 3 - a 大于 b
line 4 - a 小于或等于 b
line 5 - b 大于或等于 a
逻辑运算符
下表显示了 c# 支持的所有逻辑运算符。假设变量 a 为布尔值 true,变量 b 为布尔值 false,则:
实例
请看下面的实例,了解 c# 中所有可用的逻辑运算符:
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
bool a = true;
bool b = true;
if (a && b)
{
console.writeline("line 1 - 条件为真");
}
if (a || b)
{
console.writeline("line 2 - 条件为真");
}
/* 改变 a 和 b 的值 */
a = false;
b = true;
if (a && b)
{
console.writeline("line 3 - 条件为真");
}
else
{
console.writeline("line 3 - 条件不为真");
}
if (!(a && b))
{
console.writeline("line 4 - 条件为真");
}
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
line 1 - 条件为真
line 2 - 条件为真
line 3 - 条件不为真
line 4 - 条件为真
位运算符
位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:
假设如果 a = 60,且 b = 13,现在以二进制格式表示,它们如下所示:
a = 0011 1100
b = 0000 1101
-----------------
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011
下表列出了 c# 支持的位运算符。假设变量 a 的值为 60,变量 b 的值为 13,则:
实例
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
int a = 60; /* 60 = 0011 1100 */
int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
console.writeline("line 1 - c 的值是 {0}", c );
c = a | b; /* 61 = 0011 1101 */
console.writeline("line 2 - c 的值是 {0}", c);
c = a ^ b; /* 49 = 0011 0001 */
console.writeline("line 3 - c 的值是 {0}", c);
c = ~a; /*-61 = 1100 0011 */
console.writeline("line 4 - c 的值是 {0}", c);
c = a << 2; /* 240 = 1111 0000 */
console.writeline("line 5 - c 的值是 {0}", c);
c = a >> 2; /* 15 = 0000 1111 */
console.writeline("line 6 - c 的值是 {0}", c);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
line 1 - c 的值是 12
line 2 - c 的值是 61
line 3 - c 的值是 49
line 4 - c 的值是 -61
line 5 - c 的值是 240
line 6 - c 的值是 15
赋值运算符
下表列出了 c# 支持的赋值运算符:
实例
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
int a = 21;
int c;
c = a;
console.writeline("line 1 - = c 的值 = {0}", c);
c += a;
console.writeline("line 2 - += c 的值 = {0}", c);
c -= a;
console.writeline("line 3 - -= c 的值 = {0}", c);
c *= a;
console.writeline("line 4 - *= c 的值 = {0}", c);
c /= a;
console.writeline("line 5 - /= c 的值 = {0}", c);
c = 200;
c %= a;
console.writeline("line 6 - %= c 的值 = {0}", c);
c <<= 2;
console.writeline("line 7 - <<= c 的值 = {0}", c);
c >>= 2;
console.writeline("line 8 - >>= c 的值 = {0}", c);
c &= 2;
console.writeline("line 9 - &= c 的值 = {0}", c);
c ^= 2;
console.writeline("line 10 - ^= c 的值 = {0}", c);
c |= 2;
console.writeline("line 11 - |= c 的值 = {0}", c);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
line 1 - = c 的值 = 21
line 2 - += c 的值 = 42
line 3 - -= c 的值 = 21
line 4 - *= c 的值 = 441
line 5 - /= c 的值 = 21
line 6 - %= c 的值 = 11
line 7 - <<= c 的值 = 44
line 8 - >>= c 的值 = 11
line 9 - &= c 的值 = 2
line 10 - ^= c 的值 = 0
line 11 - |= c 的值 = 2
其他运算符
下表列出了 c# 支持的其他一些重要的运算符,包括 sizeof、typeof 和 ? :。
实例
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
/* sizeof 运算符的实例 */
console.writeline("int 的大小是 {0}", sizeof(int));
console.writeline("short 的大小是 {0}", sizeof(short));
console.writeline("double 的大小是 {0}", sizeof(double));
/* 三元运算符的实例 */
int a, b;
a = 10;
b = (a == 1) ? 20 : 30;
console.writeline("b 的值是 {0}", b);
b = (a == 10) ? 20 : 30;
console.writeline("b 的值是 {0}", b);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
int 的大小是 4
short 的大小是 2
double 的大小是 8
b 的值是 30
b 的值是 20
c#中运算符的优先级
运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。某些运算符比其他运算符有更高的优先级,例如,乘除运算符具有比加减运算符更高的优先级。
例如 x = 7 + 3 * 2,在这里,x 被赋值为 13,而不是 20,因为运算符 * 具有比 + 更高的优先级,所以首先计算乘法 3*2,然后再加上 7。
下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。
实例
using system;
namespace operatorsappl
{
class program
{
static void main(string[] args)
{
int a = 20;
int b = 10;
int c = 15;
int d = 5;
int e;
e = (a + b) * c / d; // ( 30 * 15 ) / 5
console.writeline("(a + b) * c / d 的值是 {0}", e);
e = ((a + b) * c) / d; // (30 * 15 ) / 5
console.writeline("((a + b) * c) / d 的值是 {0}", e);
e = (a + b) * (c / d); // (30) * (15/5)
console.writeline("(a + b) * (c / d) 的值是 {0}", e);
e = a + (b * c) / d; // 20 + (150/5)
console.writeline("a + (b * c) / d 的值是 {0}", e);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
(a + b) * c / d 的值是 90
((a + b) * c) / d 的值是 90
(a + b) * (c / d) 的值是 90
a + (b * c) / d 的值是 50
c#判断
判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。
判断语句
c# 提供了以下类型的判断语句。
if语句
一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
语法
c# 中 if 语句的语法:
if(boolean_expression)
{
/* 如果布尔表达式为真将执行的语句 */
}
如果布尔表达式为 true,则 if 语句内的代码块将被执行。如果布尔表达式为 false,则 if 语句结束后的第一组代码(闭括号后)将被执行。
实例
using system;
namespace decisionmaking
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* 使用 if 语句检查布尔条件 */
if (a < 20)
{
/* 如果条件为真,则输出下面的语句 */
console.writeline("a 小于 20");
}
console.writeline("a 的值是 {0}", a);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
if...else语句
一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
语法
c# 中 if...else 语句的语法:
if(boolean_expression)
{
/* 如果布尔表达式为真将执行的语句 */
}
else
{
/* 如果布尔表达式为假将执行的语句 */
}
如果布尔表达式为 true,则执行 if 块内的代码。如果布尔表达式为 false,则执行 else 块内的代码。
实例
using system;
namespace decisionmaking
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 100;
/* 检查布尔条件 */
if (a < 20)
{
/* 如果条件为真,则输出下面的语句 */
console.writeline("a 小于 20");
}
else
{
/* 如果条件为假,则输出下面的语句 */
console.writeline("a 大于 20");
}
console.writeline("a 的值是 {0}", a);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
if...else if...else 语句
一个 if 语句后可跟一个可选的 else if...else 语句,这可用于测试多种条件。
当使用 if...else if...else 语句时,以下几点需要注意:
- 一个 if 后可跟零个或一个 else,它必须在任何一个 else if 之后。
- 一个 if 后可跟零个或多个 else if,它们必须在 else 之前。
- 一旦某个 else if 匹配成功,其他的 else if 或 else 将不会被测试。
c# 中的 if...else if...else 语句的语法:
if(boolean_expression 1)
{
/* 当布尔表达式 1 为真时执行 */
}
else if( boolean_expression 2)
{
/* 当布尔表达式 2 为真时执行 */
}
else if( boolean_expression 3)
{
/* 当布尔表达式 3 为真时执行 */
}
else
{
/* 当上面条件都不为真时执行 */
}
实例
using system;
namespace decisionmaking
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 100;
/* 检查布尔条件 */
if (a == 10)
{
/* 如果 if 条件为真,则输出下面的语句 */
console.writeline("a 的值是 10");
}
else if (a == 20)
{
/* 如果 else if 条件为真,则输出下面的语句 */
console.writeline("a 的值是 20");
}
else if (a == 30)
{
/* 如果 else if 条件为真,则输出下面的语句 */
console.writeline("a 的值是 30");
}
else
{
/* 如果上面条件都不为真,则输出下面的语句 */
console.writeline("没有匹配的值");
}
console.writeline("a 的准确值是 {0}", a);
console.readline();
}
}
}
结果
没有匹配的值
a 的准确值是 100
嵌套if语句
在 c# 中,嵌套 if-else 语句是合法的,这意味着您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。
c# 中 嵌套 if 语句的语法:
if( boolean_expression 1)
{
/* 当布尔表达式 1 为真时执行 */
if(boolean_expression 2)
{
/* 当布尔表达式 2 为真时执行 */
}
}
您可以嵌套 else if...else,方式与嵌套 if 语句相似。
实例
using system;
namespace decisionmaking
{
class program
{
static void main(string[] args)
{
//* 局部变量定义 */
int a = 100;
int b = 200;
/* 检查布尔条件 */
if (a == 100)
{
/* 如果条件为真,则检查下面的条件 */
if (b == 200)
{
/* 如果条件为真,则输出下面的语句 */
console.writeline("a 的值是 100,且 b 的值是 200");
}
}
console.writeline("a 的准确值是 {0}", a);
console.writeline("b 的准确值是 {0}", b);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a 的值是 100,且 b 的值是 200
a 的准确值是 100
b 的准确值是 200
switch语句
一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查
c# 中 switch 语句的语法:
switch(expression){
case constant-expression :
statement(s);
break;
case constant-expression :
statement(s);
break;
/* 您可以有任意数量的 case 语句 */
default : /* 可选的 */
statement(s);
break;
}
switch 语句必须遵循下面的规则:
- switch 语句中的 expression 必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型。
- 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
- case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量
- 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
- 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
- 不是每一个 case 都需要包含 break。如果 case 语句为空,则可以不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。
- c# 不允许从一个 case 部分继续执行到下一个 case 部分。如果 case 语句中有已经执行,则必须包含 break 或其他跳转语句。
- 一个 switch 语句可以有一个可选的 default 语句,在 switch 的结尾。default 语句用于在上面所有 case 都不为 true 时执行的一个任务。default 也需要包含 break 语句,这是一个良好的习惯。
- c# 不支持从一个 case 标签显式贯穿到另一个 case 标签。如果要使 c# 支持从一个 case 标签显式贯穿到另一个 case 标签,可以使用 goto 一个 switch-case 或 goto default。
实例
以下实例用于判断当前是星期几:
using system;
namespace myapplication
{
class program
{
static void main(string[] args)
{
int day = 4;
switch (day)
{
case 1:
console.writeline("monday");
break;
case 2:
console.writeline("tuesday");
break;
case 3:
console.writeline("wednesday");
break;
case 4:
console.writeline("thursday");
break;
case 5:
console.writeline("friday");
break;
case 6:
console.writeline("saturday");
break;
case 7:
console.writeline("sunday");
break;
}
}
}
}
执行结果根据当天日期有所不同,我这里执行这天的结果为:
thursday
以下实例判断学生的成绩,包含了 default 语句:
using system;
namespace decisionmaking
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
char grade = 'b';
switch (grade)
{
case 'a':
console.writeline("很棒!");
break;
case 'b':
case 'c':
console.writeline("做得好");
break;
case 'd':
console.writeline("您通过了");
break;
case 'f':
console.writeline("最好再试一下");
break;
default:
console.writeline("无效的成绩");
break;
}
console.writeline("您的成绩是 {0}", grade);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
做得好
您的成绩是 b
嵌套switch语句
您可以把一个 switch 作为一个外部 switch 的语句序列的一部分,即可以在一个 switch 语句内使用另一个 switch 语句。即使内部和外部 switch 的 case 常量包含共同的值,也没有矛盾。
c# 中 嵌套 switch 语句的语法:
switch(ch1)
{
case 'a':
printf("这个 a 是外部 switch 的一部分" );
switch(ch2)
{
case 'a':
printf("这个 a 是内部 switch 的一部分" );
break;
case 'b': /* 内部 b case 代码 */
}
break;
case 'b': /* 外部 b case 代码 */
}
实例
using system;
namespace decisionmaking
{
class program
{
static void main(string[] args)
{
int a = 100;
int b = 200;
switch (a)
{
case 100:
console.writeline("这是外部 switch 的一部分");
switch (b)
{
case 200:
console.writeline("这是内部 switch 的一部分");
break;
}
break;
}
console.writeline("a 的准确值是 {0}", a);
console.writeline("b 的准确值是 {0}", b);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
这是外部 switch 的一部分
这是内部 switch 的一部分
a 的准确值是 100
b 的准确值是 200
?:运算符
我们已经在前面的章节中讲解了 条件运算符 ? :,可以用来替代 if...else 语句。它的一般形式如下:
exp1 ? exp2 : exp3;
其中,exp1、exp2 和 exp3 是表达式。请注意,冒号的使用和位置。
? 表达式的值是由 exp1 决定的。如果 exp1 为真,则计算 exp2 的值,结果即为整个 ? 表达式的值。如果 exp1 为假,则计算 exp3 的值,结果即为整个 ? 表达式的值。
c#循环
有的时候,可能需要多次执行同一块代码。一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
编程语言提供了允许更为复杂的执行路径的多种控制结构。
循环语句允许我们多次执行一个语句或语句组。
循环类型
while循环
只要给定的条件为真,c# 中的 while 循环语句会重复执行一个目标语句。、
c# 中 while 循环的语法:
while(condition)
{
statement(s);
}
在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。condition 可以是任意的表达式,当为任意非零值时都为真。当条件为真时执行循环。
当条件为假时,程序流将继续执行紧接着循环的下一条语句。
在这里,while 循环的关键点是循环可能一次都不会执行。当条件被测试且结果为假时,会跳过循环主体,直接执行紧接着 while 循环的下一条语句。
using system;
namespace loops
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* while 循环执行 */
while (a < 20)
{
console.writeline("a 的值: {0}", a);
a++;
}
console.readline();
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a 的值: 10
a 的值: 11
a 的值: 12
a 的值: 13
a 的值: 14
a 的值: 15
a 的值: 16
a 的值: 17
a 的值: 18
a 的值: 19
for/foreach循环
一个 for 循环是一个允许您编写一个执行特定次数的循环的重复控制结构。
c# 中 for 循环的语法:
for ( init; condition; increment )
{
statement(s);
}
下面是 for 循环的控制流:
-
init 会首先被执行,且只会执行一次。这一步允许您声明并初始化任何循环控制变量。您也可以不在这里写任何语句,只要有一个分号出现即可。
-
接下来,会判断 condition。如果为真,则执行循环主体。如果为假,则不执行循环主体,且控制流会跳转到紧接着 for 循环的下一条语句。
-
在执行完 for 循环主体后,控制流会跳回上面的 increment 语句。该语句允许您更新循环控制变量。该语句可以留空,只要在条件后有一个分号出现即可。
-
条件再次被判断。如果为真,则执行循环,这个过程会不断重复(循环主体,然后增加步值,再然后重新判断条件)。在条件变为假时,for 循环终止。
实例
using system;
namespace loops
{
class program
{
static void main(string[] args)
{
/* for 循环执行 */
for (int a = 10; a < 20; a = a + 1)
{
console.writeline("a 的值: {0}", a);
}
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a 的值: 10
a 的值: 11
a 的值: 12
a 的值: 13
a 的值: 14
a 的值: 15
a 的值: 16
a 的值: 17
a 的值: 18
a 的值: 19
foreach
c# 也支持 foreach 循环,使用foreach可以迭代数组或者一个集合对象。
以下实例有三个部分:
- 通过 foreach 循环输出整型数组中的元素。
- 通过 for 循环输出整型数组中的元素。
- foreach 循环设置数组元素的计算器。
实例
class foreachtest
{
static void main(string[] args)
{
int[] fibarray = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibarray)
{
system.console.writeline(element);
}
system.console.writeline();
// 类似 foreach 循环
for (int i = 0; i < fibarray.length; i++)
{
system.console.writeline(fibarray[i]);
}
system.console.writeline();
// 设置集合中元素的计算器
int count = 0;
foreach (int element in fibarray)
{
count += 1;
system.console.writeline("element #{0}: {1}", count, element);
}
system.console.writeline("number of elements in the array: {0}", count);
}
}
输出结果为:
0
1
1
2
3
5
8
13
0
1
1
2
3
5
8
13
element #1: 0
element #2: 1
element #3: 1
element #4: 2
element #5: 3
element #6: 5
element #7: 8
element #8: 13
number of elements in the array: 8
do...while循环
不像 for 和 while 循环,它们是在循环头部测试循环条件。do...while 循环是在循环的尾部检查它的条件。
do...while 循环与 while 循环类似,但是 do...while 循环会确保至少执行一次循环。
c# 中 do...while 循环的语法:
do
{
statement(s);
}while( condition );
请注意,条件表达式出现在循环的尾部,所以循环中的 statement(s) 会在条件被测试之前至少执行一次。
如果条件为真,控制流会跳转回上面的 do,然后重新执行循环中的 statement(s)。这个过程会不断重复,直到给定条件变为假为止。
实例
using system;
namespace loops
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* do 循环执行 */
do
{
console.writeline("a 的值: {0}", a);
a = a + 1;
} while (a < 20);
console.readline();
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a 的值: 10
a 的值: 11
a 的值: 12
a 的值: 13
a 的值: 14
a 的值: 15
a 的值: 16
a 的值: 17
a 的值: 18
a 的值: 19
循环控制语句
break语句
c# 中 break 语句有以下两种用法:
-
当 break 语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。
-
它可用于终止 switch 语句中的一个 case。
如果您使用的是嵌套循环(即一个循环内嵌套另一个循环),break 语句会停止执行最内层的循环,然后开始执行该块之后的下一行代码。
c# 中 break 语句的语法:
break;
实例
using system;
namespace loops
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* while 循环执行 */
while (a < 20)
{
console.writeline("a 的值: {0}", a);
a++;
if (a > 15)
{
/* 使用 break 语句终止 loop */
break;
}
}
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a 的值: 10
a 的值: 11
a 的值: 12
a 的值: 13
a 的值: 14
a 的值: 15
continue语句
c# 中的 continue 语句有点像 break 语句。但它不是强迫终止,continue 会跳过当前循环中的代码,强迫开始下一次循环。
对于 for 循环,continue 语句会导致执行条件测试和循环增量部分。对于 while 和 do...while 循环,continue 语句会导致程序控制回到条件测试上。
using system;
namespace loops
{
class program
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* do 循环执行 */
do
{
if (a == 15)
{
/* 跳过迭代 */
a = a + 1;
continue;
}
console.writeline("a 的值: {0}", a);
a++;
} while (a < 20);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a 的值: 10
a 的值: 11
a 的值: 12
a 的值: 13
a 的值: 14
a 的值: 16
a 的值: 17
a 的值: 18
a 的值: 19
无限循环
如果条件永远不为假,则循环将变成无限循环。for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。
using system;
namespace loops
{
class program
{
static void main(string[] args)
{
for (; ; )
{
console.writeline("hey! i am trapped");
}
}
}
}
当条件表达式不存在时,它被假设为真。您也可以设置一个初始值和增量表达式,但是一般情况下,程序员偏向于使用 for(;;) 结构来表示一个无限循环。
c#封装
封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。
c# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。c# 支持的访问修饰符如下所示:
- public:所有对象都可以访问;
- private:对象本身在对象内部可以访问;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;
- protected internal:访问限于当前程序集或派生自包含类的类型。
public 访问修饰符
public 访问修饰符允许一个类将其成员变量和成员函数暴露给其他的函数和对象。任何公有成员可以被外部的类访问。
下面的实例说明了这点:
using system;
namespace rectangleapplication
{
class rectangle
{
//成员变量
public double length;
public double width;
public double getarea()
{
return length * width;
}
public void display()
{
console.writeline("长度: {0}", length);
console.writeline("宽度: {0}", width);
console.writeline("面积: {0}", getarea());
}
}// rectangle 结束
class executerectangle
{
static void main(string[] args)
{
rectangle r = new rectangle();
r.length = 4.5;
r.width = 3.5;
r.display();
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在上面的实例中,成员变量 length 和 width 被声明为 public,所以它们可以被函数 main() 使用 rectangle 类的实例 r 访问。
成员函数 display() 和 getarea() 可以直接访问这些变量。
成员函数 display() 也被声明为 public,所以它也能被 main() 使用 rectangle 类的实例 r 访问。
private访问修饰符
private 访问修饰符允许一个类将其成员变量和成员函数对其他的函数和对象进行隐藏。只有同一个类中的函数可以访问它的私有成员。即使是类的实例也不能访问它的私有成员。
下面的实例说明了这点:
using system;
namespace rectangleapplication
{
class rectangle
{
//成员变量
private double length;
private double width;
public void acceptdetails()
{
console.writeline("请输入长度:");
length = convert.todouble(console.readline());
console.writeline("请输入宽度:");
width = convert.todouble(console.readline());
}
public double getarea()
{
return length * width;
}
public void display()
{
console.writeline("长度: {0}", length);
console.writeline("宽度: {0}", width);
console.writeline("面积: {0}", getarea());
}
}//end class rectangle
class executerectangle
{
static void main(string[] args)
{
rectangle r = new rectangle();
r.acceptdetails();
r.display();
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
请输入长度:
4.4
请输入宽度:
3.3
长度: 4.4
宽度: 3.3
面积: 14.52
在上面的实例中,成员变量 length 和 width 被声明为 private,所以它们不能被函数 main() 访问。
成员函数 acceptdetails() 和 display() 可以访问这些变量。
由于成员函数 acceptdetails() 和 display() 被声明为 public,所以它们可以被 main() 使用 rectangle 类的实例 r 访问。
protected访问修饰符
protected 访问修饰符允许子类访问它的基类的成员变量和成员函数。这样有助于实现继承。我们将在继承的章节详细讨论这个。更详细地讨论这个。
lnternal访问修饰符
internal 访问说明符允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。
下面的实例说明了这点:
using system;
namespace rectangleapplication
{
class rectangle
{
//成员变量
internal double length;
internal double width;
double getarea()
{
return length * width;
}
public void display()
{
console.writeline("长度: {0}", length);
console.writeline("宽度: {0}", width);
console.writeline("面积: {0}", getarea());
}
}//end class rectangle
class executerectangle
{
static void main(string[] args)
{
rectangle r = new rectangle();
r.length = 4.5;
r.width = 3.5;
r.display();
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
长度: 4.5
宽度: 3.5
面积: 15.75
在上面的实例中,请注意成员函数 getarea() 声明的时候不带有任何访问修饰符。如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。
protected lnternal访问修饰符
protected internal 访问修饰符允许在本类,派生类或者包含该类的程序集中访问。这也被用于实现继承。
(1) pubilc :任何公有成员可以被外部的类访问。
(2) private :只有同一个类中的函数可以访问它的私有成员。
(3) protected :该类内部和继承类中可以访问。
(4) internal : 同一个程序集的对象可以访问。
(5) protected internal :3 和 4 的并集,符合任意一条都可以访问。
c#方法
一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。每一个 c# 程序至少有一个带有 main 方法的类。
要使用一个方法,您需要:
- 定义方法
- 调用方法
当定义一个方法时,从根本上说是在声明它的结构的元素。在 c# 中,定义方法的语法如下:
<access specifier> <return type> <method name>(parameter list)
{
method body
}
下面是方法的各个元素:
- access specifier:访问修饰符,这个决定了变量或方法对于另一个类的可见性。
- return type:返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为 void。
- method name:方法名称,是一个唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。
- parameter list:参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和数量。参数是可选的,也就是说,一个方法可能不包含参数。
- method body:方法主体,包含了完成任务所需的指令集。
class numbermanipulator
{
public int findmax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
...
}
c#调用方法
您可以使用方法名调用方法。下面的实例演示了这点:
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public int findmax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
static void main(string[] args)
{
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret;
numbermanipulator n = new numbermanipulator();
//调用 findmax 方法
ret = n.findmax(a, b);
console.writeline("最大值是: {0}", ret );
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
最大值是: 200
您也可以使用类的实例从另一个类中调用其他类的公有方法。例如,方法 findmax 属于 numbermanipulator 类,您可以从另一个类 test 中调用它。
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public int findmax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
}
class test
{
static void main(string[] args)
{
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret;
numbermanipulator n = new numbermanipulator();
//调用 findmax 方法
ret = n.findmax(a, b);
console.writeline("最大值是: {0}", ret );
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
最大值是: 200
递归方法调用
一个方法可以自我调用。这就是所谓的 递归。下面的实例使用递归函数计算一个数的阶乘:
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public int factorial(int num)
{
/* 局部变量定义 */
int result;
if (num == 1)
{
return 1;
}
else
{
result = factorial(num - 1) * num;
return result;
}
}
static void main(string[] args)
{
numbermanipulator n = new numbermanipulator();
//调用 factorial 方法
console.writeline("6 的阶乘是: {0}", n.factorial(6));
console.writeline("7 的阶乘是: {0}", n.factorial(7));
console.writeline("8 的阶乘是: {0}", n.factorial(8));
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
6 的阶乘是: 720
7 的阶乘是: 5040
8 的阶乘是: 40320
参数传递
当调用带有参数的方法时,您需要向方法传递参数。在 c# 中,有三种向方法传递参数的方式:
按值传递参数
这是参数传递的默认方式。在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。下面的实例演示了这个概念:
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void main(string[] args)
{
numbermanipulator n = new numbermanipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
console.writeline("在交换之前,a 的值: {0}", a);
console.writeline("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(a, b);
console.writeline("在交换之后,a 的值: {0}", a);
console.writeline("在交换之后,b 的值: {0}", b);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:100
在交换之后,b 的值:200
结果表明,即使在函数内改变了值,值也没有发生任何的变化。
按引用传递参数
引用参数是一个对变量的内存位置的引用。当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 c# 中,使用 ref 关键字声明引用参数。下面的实例演示了这点:
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void main(string[] args)
{
numbermanipulator n = new numbermanipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
console.writeline("在交换之前,a 的值: {0}", a);
console.writeline("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(ref a, ref b);
console.writeline("在交换之后,a 的值: {0}", a);
console.writeline("在交换之后,b 的值: {0}", b);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:200
在交换之后,b 的值:100
结果表明,swap 函数内的值改变了,且这个改变可以在 main 函数中反映出来。
按输出传递参数
return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
下面的实例演示了这点:
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public void getvalue(out int x )
{
int temp = 5;
x = temp;
}
static void main(string[] args)
{
numbermanipulator n = new numbermanipulator();
/* 局部变量定义 */
int a = 100;
console.writeline("在方法调用之前,a 的值: {0}", a);
/* 调用函数来获取值 */
n.getvalue(out a);
console.writeline("在方法调用之后,a 的值: {0}", a);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在方法调用之前,a 的值: 100
在方法调用之后,a 的值: 5
提供给输出参数的变量不需要赋值。当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用。请看下面的实例,来理解这一点:
using system;
namespace calculatorapplication
{
class numbermanipulator
{
public void getvalues(out int x, out int y )
{
console.writeline("请输入第一个值: ");
x = convert.toint32(console.readline());
console.writeline("请输入第二个值: ");
y = convert.toint32(console.readline());
}
static void main(string[] args)
{
numbermanipulator n = new numbermanipulator();
/* 局部变量定义 */
int a , b;
/* 调用函数来获取值 */
n.getvalues(out a, out b);
console.writeline("在方法调用之后,a 的值: {0}", a);
console.writeline("在方法调用之后,b 的值: {0}", b);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果(取决于用户输入):
请输入第一个值:
7
请输入第二个值:
8
在方法调用之后,a 的值: 7
在方法调用之后,b 的值: 8
c#可空类型
c#单问号?与双问号??
? 单问号用于对 int、double、bool 等无法直接赋值为 null 的数据类型进行 null 的赋值,意思是这个数据类型是 nullable 类型的。
int? i = 3;
等同于:
nullable<int> i = new nullable<int>(3);
int i; //默认值0
int? ii; //默认值null
?? 双问号用于判断一个变量在为 null 的时候返回一个指定的值。
接下来我们详细说明。
c#可空类型
c# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。
例如,nullable< int32 >,读作"可空的 int32",可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值。类似的,nullable< bool > 变量可以被赋值为 true 或 false 或 null。
在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。
声明一个 nullable 类型(可空类型)的语法如下:
< data_type> ? <variable_name> = null;
下面的实例演示了可空数据类型的用法:
using system;
namespace calculatorapplication
{
class nullablesatshow
{
static void main(string[] args)
{
int? num1 = null;
int? num2 = 45;
double? num3 = new double?();
double? num4 = 3.14157;
bool? boolval = new bool?();
// 显示值
console.writeline("显示可空类型的值: {0}, {1}, {2}, {3}",
num1, num2, num3, num4);
console.writeline("一个可空的布尔值: {0}", boolval);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
显示可空类型的值: , 45, , 3.14157
一个可空的布尔值:
null合并运算符(??)
null 合并运算符用于定义可空类型和引用类型的默认值。null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 null。null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。下面的实例演示了这点:
using system;
namespace calculatorapplication
{
class nullablesatshow
{
static void main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
console.writeline("num3 的值: {0}", num3);
num3 = num2 ?? 5.34;
console.writeline("num3 的值: {0}", num3);
console.readline();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
num3 的值: 5.34
num3 的值: 3.14157
c#数组
数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合。
声明数组变量并不是声明 number0、number1、...、number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来表示一个个单独的变量。数组中某个指定的元素是通过索引来访问的。
所有的数组都是由连续的内存位置组成的。最低的地址对应第一个元素,最高的地址对应最后一个元素。
声明数组
在 c# 中声明一个数组,您可以使用下面的语法:
datatype[] arrayname;
其中,
- datatype 用于指定被存储在数组中的元素的类型。
- [ ] 指定数组的秩(维度)。秩指定数组的大小。
- arrayname 指定数组的名称。
例如:
double[] balance;
初始化数组
声明一个数组不会在内存中初始化数组。当初始化数组变量时,您可以赋值给数组。
数组是一个引用类型,所以您需要使用 new 关键字来创建数组的实例。
例如:
double[] balance = new double[10];
赋值给数组
您可以通过使用索引号赋值给一个单独的数组元素,比如:
double[] balance = new double[10];
balance[0] = 4500.0;
您可以在声明数组的同时给数组赋值,比如:
double[] balance = { 2340.0, 4523.69, 3421.0};
您也可以创建并初始化一个数组,比如:
int [] marks = new int[5] { 99, 98, 92, 97, 95};
在上述情况下,你也可以省略数组的大小,比如:
int [] marks = new int[] { 99, 98, 92, 97, 95};
您也可以赋值一个数组变量到另一个目标数组变量中。在这种情况下,目标和源会指向相同的内存位置:
int [] marks = new int[] { 99, 98, 92, 97, 95};
int[] score = marks;
当您创建一个数组时,c# 编译器会根据数组类型隐式初始化每个数组元素为一个默认值。例如,int 数组的所有元素都会被初始化为 0。
访问数组元素
元素是通过带索引的数组名称来访问的。这是通过把元素的索引放置在数组名称后的方括号中来实现的。例如:’
double salary = balance[9];
下面是一个实例,使用上面提到的三个概念,即声明、赋值、访问数组:
using system;
namespace arrayapplication
{
class myarray
{
static void main(string[] args)
{
int [] n = new int[10]; /* n 是一个带有 10 个整数的数组 */
int i,j;
/* 初始化数组 n 中的元素 */
for ( i = 0; i < 10; i++ )
{
n[ i ] = i + 100;
}
/* 输出每个数组元素的值 */
for (j = 0; j < 10; j++ )
{
console.writeline("element[{0}] = {1}", j, n[j]);
}
console.readkey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
element[0] = 100
element[1] = 101
element[2] = 102
element[3] = 103
element[4] = 104
element[5] = 105
element[6] = 106
element[7] = 107
element[8] = 108
element[9] = 109
使用foreach循环
在前面的实例中,我们使用一个 for 循环来访问每个数组元素。您也可以使用一个 foreach 语句来遍历数组。
using system;
namespace arrayapplication
{
class myarray
{
static void main(string[] args)
{
int [] n = new int[10]; /* n 是一个带有 10 个整数的数组 */
/* 初始化数组 n 中的元素 */
for ( int i = 0; i < 10; i++ )
{
n[i] = i + 100;
}
/* 输出每个数组元素的值 */
foreach (int j in n )
{
int i = j-100;
console.writeline("element[{0}] = {1}", i, j);
}
console.readkey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
element[0] = 100
element[1] = 101
element[2] = 102
element[3] = 103
element[4] = 104
element[5] = 105
element[6] = 106
element[7] = 107
element[8] = 108
element[9] = 109
c#字符串
在 c# 中,您可以使用字符数组来表示字符串,但是,更常见的做法是使用 string 关键字来声明一个字符串变量。string 关键字是 system.string 类的别名。
您可以使用以下方法之一来创建 string 对象:
- 通过给 string 变量指定一个字符串
- 通过使用 string 类构造函数
- 通过使用字符串串联运算符( + )
- 通过检索属性或调用一个返回字符串的方法
- 通过格式化方法来转换一个值或对象为它的字符串表示形式
下面的实例演示了这点:
using system;
namespace stringapplication
{
class program
{
static void main(string[] args)
{
//字符串,字符串连接
string fname, lname;
fname = "rowan";
lname = "atkinson";
string fullname = fname + lname;
console.writeline("full name: {0}", fullname);
//通过使用 string 构造函数
char[] letters = { 'h', 'e', 'l', 'l','o' };
string greetings = new string(letters);
console.writeline("greetings: {0}", greetings);
//方法返回字符串
string[] sarray = { "hello", "from", "tutorials", "point" };
string message = string.join(" ", sarray);
console.writeline("message: {0}", message);
//用于转化值的格式化方法
datetime waiting = new datetime(2012, 10, 10, 17, 58, 1);
string chat = string.format("message sent at {0:t} on {0:d}",
waiting);
console.writeline("message: {0}", chat);
console.readkey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
full name: rowanatkinson
greetings: hello
message: hello from tutorials point
message: message sent at 17:58 on wednesday, 10 october 2012
string类的属性
string 类有以下两个属性:
string类的方法
string 类有许多方法用于 string 对象的操作。下面的表格提供了一些最常用的方法:
实例
下面的实例演示了上面提到的一些方法:
比较字符串
using system;
namespace stringapplication
{
class stringprog
{
static void main(string[] args)
{
string str1 = "this is test";
string str2 = "this is text";
if (string.compare(str1, str2) == 0)
{
console.writeline(str1 + " and " + str2 + " are equal.");
}
else
{
console.writeline(str1 + " and " + str2 + " are not equal.");
}
console.readkey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
this is test and this is text are not equal.
字符串包含字符串:
using system;
namespace stringapplication
{
class stringprog
{
static void main(string[] args)
{
string str = "this is test";
if (str.contains("test"))
{
console.writeline("the sequence 'test' was found.");
}
console.readkey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
the sequence 'test' was found.
获取子字符串:
using system;
namespace stringapplication
{
class stringprog
{
static void main(string[] args)
{
string str = "last night i dreamt of san pedro";
console.writeline(str);
string substr = str.substring(23);
console.writeline(substr);
console.readkey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
last night i dreamt of san pedro
san pedro
连接字符串:
using system;
namespace stringapplication
{
class stringprog
{
static void main(string[] args)
{
string[] starray = new string[]{"down the way nights are dark",
"and the sun shines daily on the mountain top",
"i took a trip on a sailing ship",
"and when i reached jamaica",
"i made a stop"};
string str = string.join("\n", starray);
console.writeline(str);
console.readkey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
down the way nights are dark
and the sun shines daily on the mountain top
i took a trip on a sailing ship
and when i reached jamaica
i made a stop
c# string.format格式化日期
datetime dt = new datetime(2017,4,1,13,16,32,108);
string.format("{0:y yy yyy yyyy}",dt); //17 17 2017 2017
string.format("{0:m mm mmm mmmm}", dt);//4 04 四月 四月
string.format("{0:d dd ddd dddd}", dt);//1 01 周六 星期六
string.format("{0:t tt}", dt);//下 下午
string.format("{0:h hh}", dt);//13 13
string.format("{0:h hh}", dt);//1 01
string.format("{0:m mm}", dt);//16 16
string.format("{0:s ss}", dt);//32 32
string.format("{0:f ff fff ffff fffff ffffff fffffff}", dt);//1 1 108 108 108 108 108
string.format("{0:f ff fff ffff fffff ffffff fffffff}", dt);//1 10 108 1080 10800 108000 1080000
string.format("{0:z zz zzz}", dt);//+8 +08 +08:00
string.format("{0:yyyy/mm/dd hh:mm:ss.fff}",dt); //2017/04/01 13:16:32.108
string.format("{0:yyyy/mm/dd dddd}", dt); //2017/04/01 星期六
string.format("{0:yyyy/mm/dd dddd tt hh:mm}", dt); //2017/04/01 星期六 下午 01:16
string.format("{0:yyyymmdd}", dt); //20170401
string.format("{0:yyyy-mm-dd hh:mm:ss.fff}", dt); //2017-04-01 13:16:32.108
除去string.format()可以对日期进行格式化之外,*.tostring()也可以实现相同的效果:
datetime dt = new datetime(2017,4,1,13,16,32,108);
dt.tostring("y yy yyy yyyy");//17 17 2017 2017
dt.tostring("m mm mmm mmmm");//4 04 四月 四月
dt.tostring("d dd ddd dddd");//1 01 周六 星期六
dt.tostring("t tt");//下 下午
dt.tostring("h hh");//13 13
dt.tostring("h hh");//1 01
dt.tostring("m mm");//16 16
dt.tostring("s ss");//32 32
dt.tostring("f ff fff ffff fffff ffffff fffffff");//1 1 108 108 108 108 108
dt.tostring("f ff fff ffff fffff ffffff fffffff");//1 10 108 1080 10800 108000 1080000
dt.tostring("z zz zzz");//+8 +08 +08:00
dt.tostring("yyyy/mm/dd hh:mm:ss.fff"); //2017/04/01 13:16:32.108
dt.tostring("yyyy/mm/dd dddd"); //2017/04/01 星期六
dt.tostring("yyyy/mm/dd dddd tt hh:mm"); //2017/04/01 星期六 下午 01:16
dt.tostring("yyyymmdd"); //20170401
dt.tostring("yyyy-mm-dd hh:mm:ss.fff"); //2017-04-01 13:16:32.108
c#结构体
在 c# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
结构体是用来代表一个记录。假设您想跟踪图书馆中书的动态。您可能想跟踪每本书的以下属性:
- title
- author
- subject
- book id
定义结构体
为了定义一个结构体,您必须使用 struct 语句。struct 语句为程序定义了一个带有多个成员的新的数据类型。
例如,您可以按照如下的方式声明 book 结构:
struct books
{
public string title;
public string author;
public string subject;
public int book_id;
};
下面的程序演示了结构的用法:
using system;
using system.text;
namespace day01
{
struct books
{
public string title;
public string author;
public string subject;
public int book_id;
};
class program
{
static void main(string[] args)
{
books book1;
book1.title = "c programming";
book1.author = "nuha ali";
book1.subject = "c programming tutorial";
book1.book_id = 12345;
console.writeline("book1 title:{0}", book1.title);
console.writeline("book1 author:{0}", book1.author);
console.writeline("book1 subject:{0}", book1.subject);
console.writeline("book1 book_id:{0}", book1.book_id);
console.readkey();
}
}
}
运行结果:
c#结构的特点
您已经用了一个简单的名为 books 的结构。在 c# 中的结构与传统的 c 或 c++ 中的结构不同。c# 中的结构有以下特点:
- 结构可带有方法、字段、索引、属性、运算符方法和事件。
- 结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数(默认)是自动定义的,且不能被改变。
- 与类不同,结构不能继承其他的结构或类。
- 结构不能作为其他结构或类的基础结构。
- 结构可实现一个或多个接口。
- 结构成员不能指定为 abstract、virtual 或 protected。
- 当您使用 new 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 new 操作符即可被实例化。
- 如果不使用 new 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
类 vs 结构
类和结构有以下几个基本的不同点:
- 类是引用类型,结构是值类型。
- 结构不支持继承。
- 结构不能声明默认的构造函数。
针对上述讨论,让我们重写前面的实例:
using system;
using system.text;
namespace day01
{
struct books
{
private string title;
private string author;
private string subject;
private int book_id;
public void setvalues(string t,string a,string s,int id)
{
title = t;
author = a;
subject = s;
book_id = id;
}
public void display()
{
console.writeline("title:{0}", title);
console.writeline("author:{0}", author);
console.writeline("subject:{0}", subject);
console.writeline("book_id:{0}", book_id);
}
};
class program
{
static void main(string[] args)
{
books book1 = new books();
book1.setvalues("c programming", "qk", "c programming tutorial", 12345);
book1.display();
console.readkey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
补充:类与结构体的区别
1、结构体中声明的字段无法赋予初值,类可以:
struct test001
{
private int aa = 1;
}
执行以上代码将出现“结构中不能实例属性或字段初始值设定”的报错,而类中无此限制,代码如下:
class test002
{
private int aa = 1;
}
2、结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制:
补充:类与结构的选择
首先明确,类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。当然,这也得从需求出发,假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。
结构和类的区别:
- 结构是值类型,它在栈中分配空间;而类是引用类型,它在堆中分配空间,栈中保存的只是引用。
- 结构类型直接存储成员数据,让其他类的数据位于堆中,位于栈中的变量保存的是指向堆中数据对象的引用。
c# 中的简单类型,如int、double、bool等都是结构类型。如果需要的话,甚至可以使用结构类型结合运算符运算重载,再为 c# 语言创建出一种新的值类型来。
由于结构是值类型,并且直接存储数据,因此在一个对象的主要成员为数据且数据量不大的情况下,使用结构会带来更好的性能。
因为结构是值类型,因此在为结构分配内存,或者当结构超出了作用域被删除时,性能会非常好,因为他们将内联或者保存在堆栈中。当把一个结构类型的变量赋值给另一个结构时,对性能的影响取决于结构的大小,如果结构的数据成员非常多而且复杂,就会造成损失,接下来使用一段代码来说明这个问题。
结构和类的适用场合分析:
- 当堆栈的空间很有限,且有大量的逻辑对象时,创建类要比创建结构好一些;
- 对于点、矩形和颜色这样的轻量对象,假如要声明一个含有许多个颜色对象的数组,则clr需要为每个对象分配内存,在这种情况下,使用结构的成本较低;
- 在表现抽象和多级别的对象层次时,类是最好的选择,因为结构不支持继承。
- 大多数情况下,目标类型只是含有一些数据,或者以数据为主
c#枚举
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
c# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承
声明enum变量
声明枚举的一般语法:
enum <enum_name>
{
enumeration list
};
其中,
- enum_name指定枚举的类型名称
- enumeration list是一个用逗号分隔的标识符列表。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值,默认情况下,第一个枚举符号的值是0,例如:
enum days { sun, mon, tue, wed, thu, fri, sat };
实例:
using system;
namespace day01
{
class program
{
enum day {sun,mon,tue,wed,thu,fri,sat};
static void main(string[] args)
{
int x = (int)day.sun;
int y = (int)day.fri;
console.writeline("sun={0}\nfri={1}",x,y);
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
发表评论