文章目录
- 编译/执行 C# 程序
- C# 基本语法
- 字符串(String)类型
- 指针类型(Pointer types)
- C# 类型转换
- 接受来自用户的值
- c#输出语句
- 常量:const readonly
- 位运算符:&、 | 和 ^
- 参数传递:值传递 引用传递(ref) 按输出传递参数
- C# 可空类型(Nullable)?
- 使用 foreach 循环:(int j in n)
- C# 结构体(Struct)
- C# 枚举(Enum)
- C# 中的析构函数
- C# 继承:用的是:不是extend
- C# 多重继承:C# 不支持多重继承。可以使用接口来实现多重继承。
- C# 运算符重载:operator+-*/%==, !=, <, >, <=, >=
- C# 命名空间(Namespace)(using)
- using 关键字:跟java导包是一样的,命名空间就是包名
- 嵌套命名空间
- C# 预处理器指令:#define等
- C# 文件的输入与输出:IO
- C# 特性(Attribute):[xxx]
- C# 反射(Reflection)
- 反射中 GetCustomAttributes
- C#域(Field)
- C# 属性(Property):就是那些{get;set}
- C# 索引器(Indexer)
- C# 委托(Delegate):类似java代理;相当于一个引用类型的变量
- 委托代理实现
- C# 事件(Event)
- C# 泛型(Generic)
- C# 匿名方法
- C# 不安全代码:指针 type*
- C# 多线程
编译/执行 C# 程序
using System;
namespace HelloWorldApplication
{
/* 类名为 HelloWorld */
class HelloWorld
{
/* main函数 */
static void Main(string[] args)
{
/* 我的第一个 C# 程序 */
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
}
程序的第一行 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# 基本语法
```csharp
using System;
namespace RectangleApplication
{
class Rectangle
{
// 成员变量
double length;
double width;
public void Acceptdetails()
{
length = 4.5;
width = 3.5;
}
public double 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();
}
}
}
Length: 4.5
Width: 3.5
Area: 15.75
字符串(String)类型
字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
String str = "runoob.com";
String str = @"runoob.com";
C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待
比如:
string str = @"C:\Windows";
等价于:
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"<script type=""text/javascript"">
<!--
-->
</script>";
指针类型(Pointer types)
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:
type* identifier;
例如:
char* cptr;
int* iptr;
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# 类型转换方法
1 ToBoolean
如果可能的话,把类型转换为布尔型。
2 ToByte
把类型转换为字节类型。
3 ToChar
如果可能的话,把类型转换为单个 Unicode 字符类型。
4 ToDateTime
把类型(整数或字符串类型)转换为 日期-时间 结构。
5 ToDecimal
把浮点型或整数类型转换为十进制类型。
6 ToDouble
把类型转换为双精度浮点型。
7 ToInt16
把类型转换为 16 位整数类型。
10 ToSbyte
把类型转换为有符号字节类型。
11 ToSingle
把类型转换为小浮点数类型。
12 ToString
把类型转换为字符串类型。
13 ToType
把类型转换为指定类型。
下面的实例把不同值的类型转换为字符串类型:
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
接受来自用户的值
System 命名空间中的 Console 类提供了一个函数 ReadLine(),用于接收来自用户的输入
并把它存储到一个变量中。
例如:
int num;
num = Convert.ToInt32(Console.ReadLine());
函数 Convert.ToInt32() 把用户输入的数据转换为 int 数据类型
因为 Console.ReadLine() 只接受字符串格式的数据。
这种的也是输入一个值,但是没有接受值,相当于按任意键结束的那个意思;
Console.ReadLine();
c#输出语句
{0}:代表后面参数的索引,是第几个参数,当然正常输出参数也是没问题的
Console.WriteLine("Full Name: {0}", fullname);
常量:const readonly
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);
//将mC.x, mC.y,放到 0 和 1的位置,下面同理
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
1.静态常量(编译时常量)const
在编译时就确定了值,必须在声明时就进行初始化且之后不能进行更改,可在类和方法中定义。定义方法如下:
const double a=3.14;// 正确声明常量的方法
const int b; // 错误,没有初始化
2.动态常量(运行时常量)readonly
在运行时确定值,只能在声明时或构造函数中初始化,只能在类中定义。定义方法如下:
class Program
{
readonly int a=1; // 声明时初始化
readonly int b; // 构造函数中初始化
Program()
{
b=2;
}
static void Main()
{
}
}
位运算符:&、 | 和 ^
位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:
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
参数传递:值传递 引用传递(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);
//java中,a b是不会变的,就是值传递,这里会变
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:200
在交换之后,b 的值:100
按值传递参数:java的传递参数
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
按输出传递参数
return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValue(out int x )
{
int temp = 5;
//会把最后的x out出去,相当于return,如果还有一个return,就可以返回2个值了
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
C# 可空类型(Nullable)?
C# 单问号 ? 与 双问号 ??
? : 单问号用于对 int,double,bool 等无法直接赋值为 null 的数据类型进行 null 的赋值
意思是这个数据类型是 Nullable 类型的。
int? i = 3;
等同于:
Nullable<int> i = new Nullable<int>(3);
int i; //默认值0
int? ii; //默认值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
使用 foreach 循环:(int j in n)
/* 输出每个数组元素的值 */
foreach (int j in n )
{
int i = j-100;
Console.WriteLine("Element[{0}] = {1}", i, j);
}
Console.ReadKey();
C# 结构体(Struct)
类 vs 结构
类和结构有以下几个基本的不同点:
类是引用类型,结构是值类型。
结构不支持继承。
结构不能声明默认的构造函数。
结构体中声明的字段无法赋予初值,类可以
类的对象是存储在堆空间中,结构存储在栈中。
堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
using System;
using System.Text;
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);
}
};
public class testStructure
{
public static void Main(string[] args)
{
Books Book1 = new Books(); /* 声明 Book1,类型为 Books */
Books Book2 = new Books(); /* 声明 Book2,类型为 Books */
/* book 1 详述 */
Book1.setValues("C Programming",
"Nuha Ali", "C Programming Tutorial",6495407);
/* book 2 详述 */
Book2.setValues("Telecom Billing",
"Zara Ali", "Telecom Billing Tutorial", 6495700);
/* 打印 Book1 信息 */
Book1.display();
/* 打印 Book2 信息 */
Book2.display();
Console.ReadKey();
}
}
C# 枚举(Enum)
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。
默认情况下,第一个枚举符号的值是 0.例如:
enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };
using System;
public class EnumTest
{
enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
static void Main()
{
int x = (int)Day.Sun;
int y = (int)Day.Fri;
Console.WriteLine("Sun = {0}", x);
Console.WriteLine("Fri = {0}", y);
}
}
Sun = 0
Fri = 5
using System;
namespace EnumApplication
{
class EnumProgram
{
enum Days {
Mon=71,
tue=61,
Wed=51,
thu=41,
Fri=51,
Sat=61,
Sun=71
};
static void Main(string[] args)
{
int WeekdayStart = (int)Days.Mon;
int WeekdayEnd = (int)Days.Fri;
Console.WriteLine("Monday: {0}", WeekdayStart);
Console.WriteLine("Friday: {0}", WeekdayEnd);
Console.ReadKey();
}
}
}
Monday: 71
Friday: 51
C# 中的析构函数
类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
using System;
namespace LineApplication
{
class Line
{
private double length; // 线条的长度
public Line() // 构造函数
{
Console.WriteLine("对象已创建");
}
~Line() //析构函数
{
Console.WriteLine("对象已删除");
}
public void setLength( double len )
{
length = len;
}
public double getLength()
{
return length;
}
static void Main(string[] args)
{
Line line = new Line();
// 设置线条长度
line.setLength(6.0);
Console.WriteLine("线条的长度: {0}", line.getLength());
}
}
}
对象已创建
线条的长度: 6
对象已删除
C# 继承:用的是:不是extend
<访问修饰符符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}
using System;
namespace InheritanceApplication
{
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}
// 派生类
class Rectangle: Shape
{
public int getArea()
{
return (width * height);
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
Rect.setWidth(5);
Rect.setHeight(7);
// 打印对象的面积
Console.WriteLine("总面积: {0}", Rect.getArea());
Console.ReadKey();
}
}
}
总面积: 35
C# 多重继承:C# 不支持多重继承。可以使用接口来实现多重继承。
using System;
namespace InheritanceApplication
{
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}
// 接口 PaintCost
public interface PaintCost
{
int getCost(int area);
}
// 派生类
class Rectangle : Shape, PaintCost
{
public int getArea()
{
return (width * height);
}
public int getCost(int area)
{
return area * 70;
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 打印对象的面积
Console.WriteLine("总面积: {0}", Rect.getArea());
Console.WriteLine("油漆总成本: ${0}" , Rect.getCost(area));
Console.ReadKey();
}
}
}
总面积: 35
油漆总成本: $2450
C# 运算符重载:operator±*/%==, !=, <, >, <=, >=
定义了这个,之后两个对象实例就可以相加:box1+box2就不会报错,其他符号同理
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
using System;
namespace OperatorOvlApplication
{
class Box
{
private double length; // 长度
private double breadth; // 宽度
private double height; // 高度
public double getVolume()
{
return length * breadth * height;
}
public void setLength( double len )
{
length = len;
}
public void setBreadth( double bre )
{
breadth = bre;
}
public void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符来把两个 Box 对象相加
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
}
class Tester
{
static void Main(string[] args)
{
Box Box1 = new Box(); // 声明 Box1,类型为 Box
Box Box2 = new Box(); // 声明 Box2,类型为 Box
Box Box3 = new Box(); // 声明 Box3,类型为 Box
double volume = 0.0; // 体积
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
Console.WriteLine("Box1 的体积: {0}", volume);
// Box2 的体积
volume = Box2.getVolume();
Console.WriteLine("Box2 的体积: {0}", volume);
// 把两个对象相加
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
Console.WriteLine("Box3 的体积: {0}", volume);
Console.ReadKey();
}
}
}
Box1 的体积: 210
Box2 的体积: 1560
Box3 的体积: 5400
C# 命名空间(Namespace)(using)
下面的程序演示了命名空间的用法:
using System;
namespace first_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside first_space");
}
}
}
namespace second_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
class TestClass
{
static void Main(string[] args)
{
first_space.namespace_cl fc = new first_space.namespace_cl();
second_space.namespace_cl sc = new second_space.namespace_cl();
fc.func();
sc.func();
Console.ReadKey();
}
}
Inside first_space
Inside second_space
using 关键字:跟java导包是一样的,命名空间就是包名
using 关键字表明程序使用的是给定命名空间中的名称。
例如,我们在程序中使用 System 命名空间,其中定义了类 Console。我们可以只写:
Console.WriteLine ("Hello there");
我们可以写完全限定名称,如下:
System.Console.WriteLine("Hello there");
您也可以使用 using 命名空间指令,这样在使用的时候就不用在前面加上命名空间名称。
该指令告诉编译器随后的代码使用了指定命名空间中的名称。下面的代码演示了命名空间的应用。
using System;
using first_space;
using second_space;
namespace first_space
{
class abc
{
public void func()
{
Console.WriteLine("Inside first_space");
}
}
}
namespace second_space
{
class efg
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
class TestClass
{
static void Main(string[] args)
{
abc fc = new abc();
efg sc = new efg();
fc.func();
sc.func();
Console.ReadKey();
}
}
Inside first_space
Inside second_space
嵌套命名空间
命名空间可以被嵌套,即您可以在一个命名空间内定义另一个命名空间,如下所示:
namespace namespace_name1
{
// 代码声明
namespace namespace_name2
{
// 代码声明
}
}
您可以使用点(.)运算符访问嵌套的命名空间的成员
using System;
using SomeNameSpace;
using SomeNameSpace.Nested;
namespace SomeNameSpace
{
public class MyClass
{
static void Main()
{
Console.WriteLine("In SomeNameSpace");
Nested.NestedNameSpaceClass.SayHello();
}
}
// 内嵌命名空间
namespace Nested
{
public class NestedNameSpaceClass
{
public static void SayHello()
{
Console.WriteLine("In Nested");
}
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
In SomeNameSpace
In Nested
C# 预处理器指令:#define等
预处理器指令指导编译器在实际编译开始之前对信息进行预处理。
#define 它用于定义一系列成为符号的字符。
#if 它用于测试符号是否为真。
#else 它用于创建复合条件指令,与 #if 一起使用。
#error 它允许从代码的指定位置生成一个错误。
#define 预处理器
#define 预处理器指令创建符号常量。
#define 允许您定义一个符号,这样,通过使用符号作为传递给 #if 指令的表达式,表达式将返回 true。
它的语法如下:
#define symbol
#define PI
using System;
namespace PreprocessorDAppl
{
class Program
{
static void Main(string[] args)
{
#if (PI)
Console.WriteLine("PI is defined");
#else
Console.WriteLine("PI is not defined");
#endif
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
PI is defined
#define DEBUG
#define VC_V10
using System;
public class TestClass
{
public static void Main()
{
#if (DEBUG && !VC_V10)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && VC_V10)
Console.WriteLine("VC_V10 is defined");
#elif (DEBUG && VC_V10)
Console.WriteLine("DEBUG and VC_V10 are defined");
#else
Console.WriteLine("DEBUG and VC_V10 are not defined");
#endif
Console.ReadKey();
}
}
当上面的代码被编译和执行时,它会产生下列结果:只能执行第三个判断
DEBUG and VC_V10 are defined
C# 文件的输入与输出:IO
BinaryReader 从二进制流读取原始数据。
BinaryWriter 以二进制格式写入原始数据。
BufferedStream 字节流的临时存储。
File 有助于处理文件。
FileInfo 用于对文件执行操作。
FileStream 用于文件中任何位置的读写。
MemoryStream 用于随机访问存储在内存中的数据流。
Path 对路径信息执行操作。
StreamReader 用于从字节流中读取字符。
StreamWriter 用于向一个流中写入字符。
StringReader 用于读取字符串缓冲区。
StringWriter 用于写入字符串缓冲区。
FileStream 类
FileStream <object_name> = new FileStream( <file_name>,
<FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);
FileMode
FileMode 枚举定义了各种打开文件的方法。FileMode 枚举的成员有:
Append:打开一个已有的文件,并将光标放置在文件的末尾。如果文件不存在,则创建文件。
Create:创建一个新的文件。如果文件已存在,则删除旧文件,然后创建新文件。
CreateNew:指定操作系统应创建一个新的文件。如果文件已存在,则抛出异常。
Open:打开一个已有的文件。如果文件不存在,则抛出异常。
OpenOrCreate:指定操作系统应打开一个已有的文件。如果文件不存在,则用指定的名称创建一个新的文件打开。
Truncate:打开一个已有的文件,文件一旦打开,就将被截断为零字节大小。
然后我们可以向文件写入全新的数据,但是保留文件的初始创建日期。如果文件不存在,则抛出异常。
FileAccess
FileAccess 枚举的成员有:Read、ReadWrite 和 Write。
FileShare
FileShare 枚举的成员有:
Inheritable:允许文件句柄可由子进程继承。Win32 不直接支持此功能。
None:谢绝共享当前文件。文件关闭前,打开该文件的任何请求(由此进程或另一进程发出的请求)都将失败。
Read:允许随后打开文件读取。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取的请求(由此进程或另一进程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
ReadWrite:允许随后打开文件读取或写入。
如果未指定此标志,则文件关闭前,任何打开该文件以进行读取或写入的请求(由此进程或另一进程发出)都将失败。
但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
Write:允许随后打开文件写入。
如果未指定此标志,则文件关闭前,任何打开该文件以进行写入的请求(由此进程或另一进过程发出的请求)都将失败。
但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
Delete:允许随后删除文件。
using System;
using System.IO;
namespace FileIOApplication
{
class Program
{
static void Main(string[] args)
{
FileStream F = new FileStream("test.dat",
FileMode.OpenOrCreate, FileAccess.ReadWrite);
for (int i = 1; i <= 20; i++)
{
F.WriteByte((byte)i);
}
F.Position = 0;
for (int i = 0; i <= 20; i++)
{
Console.Write(F.ReadByte() + " ");
}
F.Close();
Console.ReadKey();
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -1
C# 特性(Attribute):[xxx]
规定特性(Attribute)
规定特性(Attribute)的语法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。
positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
预定义特性(Attribute)
.Net 框架提供了三种预定义特性:
AttributeUsage
Conditional
Obsolete
AttributeUsage
预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。
规定该特性的语法如下:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
其中:
参数 validon 规定特性可被放置的语言元素。
它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All(所有)。
参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。
如果为 true,则该特性是多用的。默认值是 false(单用的)。
参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。
如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。
例如:第一个属性用|隔开表示多个,第二个属性true是多用,这里没写第三个属性,默认不能继承
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
Conditional:条件
这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。
它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。//Trace:跟踪
例如,当调试代码时显示变量的值。
规定该特性的语法如下:
[Conditional(conditionalSymbol)] //symbol:符号
例如:
[Conditional("DEBUG")]
实例
#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void function1()
{
Myclass.Message("In Function 1.");
function2();
}
static void function2()
{
Myclass.Message("In Function 2.");
}
public static void Main()
{
Myclass.Message("In Main function.");
function1();
Console.ReadKey();
}
}
当上面的代码被编译和执行时,它会产生下列结果:
In Main function
In Function 1
In Function 2
Obsolete:过时的
当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法
您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
规定该特性的语法如下:
[Obsolete(message)]
[Obsolete(
message,
iserror
)]
其中:
参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。
默认值是 false(编译器生成一个警告)。
实例
using System;
public class MyClass
{
[Obsolete("不要使用OldMethod, 相反使用要NewMethod", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
不要使用OldMethod, 相反使用要NewMethod
声明自定义特性
一个新的自定义特性应派生自 System.Attribute 类。例如:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage
//定义了特性能被放在那些前面
(AttributeTargets.Class |//规定了特性能被放在class的前面
AttributeTargets.Constructor |//规定了特性能被放在构造函数的前面
AttributeTargets.Field |//规定了特性能被放在域的前面
AttributeTargets.Method |//规定了特性能被放在方法的前面
AttributeTargets.Property,//规定了特性能被放在属性的前面
AllowMultiple = true)]//这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。
public class DeBugInfo : System.Attribute
声明了一个名为 DeBugInfo 的自定义特性。
创建自定义特性(Attribute)
所谓的自定义消息通过 AttributeUsage 进行定义
通常情况下就是写一个类继承自 Attribute,当然标签特性也要加上。
1、创建一个自定义特性:
// 描述如何使用一个自定义特性 SomethingAttribute
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
//********自定义特性SomethingAttribute**************//
public class SomethingAttribute : Attribute {
private string name; // 名字
private string data; // 日期
public string Name {
get { return name; }
set { name = value; }
}
public string Data {
get { return data; }
set { data = value; }
}
public SomethingAttribute(string name) {
this.name = name;
this.name = name;
}
}
2、实例化自定义
[Something("Amy", Data = "2018-06-18")]
[Something("Jack", Data = "2018-06-18")]
class Test{}
3、获取自定义特性的中的变量
Type t = typeof(Test);
var something = t.GetCustomAttributes(typeof(SomethingAttribute),true);
foreach(SomethingAttribute each in something)
{
Console.WriteLine("Name:{0}", each.Name);
Console.WriteLine("Data:{0}",each.Data);
}
#define BUG
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;
namespace helloworld
{
public class Myclass
{
[Conditional("BUG")]
public static void M1(string msg)
{
Console.WriteLine(msg);
}
[Conditional("deBUG")]
public static void M2(string msg)
{
Console.WriteLine(msg);
}
[Conditional("BUG")]
public static void M3(string msg)
{
Console.WriteLine(msg);
}
[Conditional("kk")]
public static void M4(string msg)
{
Console.WriteLine(msg);
}
}
class Program
{
static void Main(string[] args)
{
Myclass.M1("456");
Myclass.M2("845");
Myclass.M3("dadasd");
Myclass.M4("456xcvc");
Console.WriteLine("Main thread ID is:" );
Console.ReadKey();
}
}
}
将头部的#define BUG就会运行M1和M3,定义为#define kk就将会只运行M4方法。
但是注意如果没有写[]的都会执行,所以一般加[]attrible时候,常常看见如果有加一般每个方法上面都会加。
C# 反射(Reflection)
反射中 GetCustomAttributes
public abstract object[] GetCustomAttributes(bool inherit);
这是GetCustomAttributes方法的一个重载,参数为bool类型
返回一个object数组,用以保存对象中的自定义属性(attribute)
参数为true时包括子类中的自定义属性
using System;
using System.Reflection;
namespace BugFixApplication
{
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage
//定义了特性能被放在那些前面
(AttributeTargets.Class |//规定了特性能被放在class的前面
AttributeTargets.Constructor |//规定了特性能被放在构造函数的前面
AttributeTargets.Field |//规定了特性能被放在域的前面
AttributeTargets.Method |//规定了特性能被放在方法的前面
AttributeTargets.Property,//规定了特性能被放在属性的前面
AllowMultiple = true)]//这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
[DeBugInfo(45, "Zara Ali", "12/8/2012",
Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012",
Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}//end class Rectangle
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(4.5, 7.5);
r.Display();
Type type = typeof(Rectangle);
// 遍历 Rectangle 类的特性,false不获取子类的特征
foreach (Object attributes in type.GetCustomAttributes(false))
{
DeBugInfo dbi = (DeBugInfo)attributes;
if (null != dbi)
{
//如果直接dbi,就是打印特征的名字,不包括属性
//例如Console.WriteLine("Bug no: {0}", attributes);结果就是DeBugInfo,有多个就打印多个
//打印特征的属性,此时是不包括方法上面添加的那些属性的
Console.WriteLine("Bug no: {0}", dbi.BugNo);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}",
dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
// 遍历方法特性
foreach (MethodInfo m in type.GetMethods())
{
foreach (Attribute a in m.GetCustomAttributes(true))
{
//as适用于引用类型的转换,数值的还是得用()强制,c#中()强制转化是不安全的要try
//C#中引用类型可以用as来强制转型,再赋值,也是一种强转,写法与java不一样,
DeBugInfo dbi = a as DeBugInfo;
if (null != dbi)
{
//指定了方法,打印的是方法上添加的属性
Console.WriteLine("Bug no: {0}, for Method: {1}",
dbi.BugNo, m.Name);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}",
dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
}
Console.ReadLine();
}
}
}
//第一次打印
Length: 4.5
Width: 7.5
Area: 33.75
//打印Attributes,不打印方法
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2012
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2012
Remarks: Return type mismatch
//指定打印方法
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks:
C#域(Field)
类或结构中的成员变量或方法称为 域(Field)。
C# 属性(Property):就是那些{get;set}
属性(Property) 是类(class)、结构(structure)和接口(interface)的命名(named)成员。
类或结构中的成员变量或方法称为 域(Field)。
属性(Property)是域(Field)的扩展,且可使用相同的语法来访问。
它们使用 访问器(accessors) 让私有域的值可被读写或操作。
属性(Property)不会确定存储位置。
相反,它们具有可读写或计算它们值的 访问器(accessors)就是get,set。
例如,有一个名为 Student 的类,带有 age、name 和 code 的私有域。
我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。
using System;
namespace runoob
{
class Student
{
private string code = "N.A";
private string name = "not known";
private int age = 0;
// 声明类型为 string 的 Code 属性
public string Code
{
get//访问器
{
return code;
}
set//访问器
{
code = value;
}
}
// 声明类型为 string 的 Name 属性
public string Name
{
get//访问器
{
return name;
}
set//访问器
{
name = value;
}
}
// 声明类型为 int 的 Age 属性
public int Age
{
get//访问器
{
return age;
}
set//访问器
{
age = value;
}
}
public override string ToString()
{
return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
}
}
class ExampleDemo
{
public static void Main()
{
// 创建一个新的 Student 对象
Student s = new Student();
// 可以直接设置这些属性,虽然他们是private的,但是可以通过那些get;set设置
// 设置 student 的 code、name 和 age
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info: {0}", s);
// 增加年龄
s.Age += 1;
Console.WriteLine("Student Info: {0}", s);
Console.ReadKey();
}
}
}
Student Info: Code = 001, Name = Zara, Age = 9
Student Info: Code = 001, Name = Zara, Age = 10
简化
class Program
{
public abstract class Person
{
public abstract string Name { get; set; }
public abstract int Age { get; set; }
}
public class Student : Person
{
public string Code { get; set; } = "N.A";
public override string Name { get; set; } = "N.A";
public override int Age { get; set; } = 0;
public override string ToString()
{
return $"Code:{Code},Name:{Name},Age:{Age}";
}
}
C# 索引器(Indexer)
语法
一维索引器的语法如下:
element-type this[int index]
{
// get 访问器
get
{
// 返回 index 指定的值
}
// set 访问器
set
{
// 设置 index 指定的值
}
}
using System;
namespace IndexerApplication
{
class IndexedNames
{
private string[] namelist = new string[size];
static public int size = 10;
public IndexedNames()
{
for (int i = 0; i < size; i++)
namelist[i] = "N. A.";
}
//按照指定的参数执行,赋值是默认走set,变量默认走get
public string this[int index]
{
get
{//get是默认调用的,names[i]就等于names[i].get,与tostring类似
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
//return返回,这里我们是用Console.WriteLine(names[i]);返回就输出了
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
//按照索引走set方法,相当于names[0].set("Zara");
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
names[3] = "Asif";
names[4] = "Davinder";
names[5] = "Sunil";
names[6] = "Rubic";
for ( int i = 0; i < IndexedNames.size; i++ )
{
//按照索引走get方法,默认调用了get,与默认调用tostring一样
//返回的值作为参数writh输出
Console.WriteLine(names[i]);
}
Console.ReadKey();
}
}
}
Zara
Riz
Nuha
Asif
Davinder
Sunil
Rubic
N. A.
N. A.
N. A.
还可以用string做索引
public int this[string name]
{
get
{
int index = 0;
while(index < size)
{
if (namelist[index] == name)
{
return index;
}
index++;
}
return index;
}
// 使用带有 string 参数的第二个索引器
//找到对应string的索引位置
Console.WriteLine(names["Nuha"]);
C# 委托(Delegate):类似java代理;相当于一个引用类型的变量
实例
using System;
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
// 使用委托对象调用方法,可以用委托传递参数到方法中
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
Value of Num: 35
Value of Num: 175
委托的多播:就是可以将委托的实例进行+_*/
static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
// 调用多播
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
注意有顺序:先执行nc1再 + nc2
Value of Num: 75
nc = nc2;
nc += nc1;
先执行nc2 再 + nc1
Value of Num: 55
委托代理实现
using System;
using System.IO;
namespace DelegateAppl
{
class PrintString
{
static FileStream fs;
static StreamWriter sw;
// 委托声明
public delegate void printString(string s);
// 该方法打印到控制台
public static void WriteToScreen(string str)
{
Console.WriteLine("The String is: {0}", str);
}
// 该方法打印到文件
public static void WriteToFile(string s)
{
fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
// 该方法把委托作为参数,并使用它调用方法
public static void sendString(printString ps)
{
ps("Hello World");
}
static void Main(string[] args)
{
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
sendString(ps1);
sendString(ps2);
Console.ReadKey();
}
}
}
The String is: Hello World
委托多播实例:例如小明叫小张买完车票,之后接着又让他带张电影票:
// 小张类
public class MrZhang
{
// 其实买车票的悲情人物是小张
public static void BuyTicket()
{
Console.WriteLine("NND,每次都让我去买票,鸡人呀!");
}
public static void BuyMovieTicket()
{
Console.WriteLine("我去,自己泡妞,还要让我带电影票!");
}
}
//小明类
class MrMing
{
// 声明一个委托,其实就是个“命令”
public delegate void BugTicketEventHandler();
public static void Main(string[] args)
{
// 这里就是具体阐述这个命令是干什么的,本例是MrZhang.BuyTicket“小张买车票”
BugTicketEventHandler myDelegate = new BugTicketEventHandler(MrZhang.BuyTicket);
myDelegate += MrZhang.BuyMovieTicket;
// 这时候委托被附上了具体的方法,没有参数也会执行方法
//前面只是定义,这里才是使用,没有参数说明两个方法都不需要参数
myDelegate();
Console.ReadKey();
}
}
C# 事件(Event)
事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等
或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。
delegate 相当于定义一个函数类型。
event 相当于定义一个 delegate 的函数指针(回调函数指针)。
声明事件(Event)
在类的内部声明事件,首先必须声明该事件的委托类型。例如:
public delegate void BoilerLogHandler(string status);
然后,声明事件本身,使用 event 关键字:
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
上面的代码定义了一个名为 BoilerLogHandler 的委托和一个名为 BoilerEventLog 的事件
该事件在生成的时候会调用委托。
实例
using System;
namespace SimpleEvent
{
using System;
/***********发布器类***********/
public class EventTest
{
//value是0
private int value;
public delegate void NumManipulationHandler();
//现在是null
public event NumManipulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if ( ChangeNum != null )
{
//前面说了事件在生成的时候会调用委托,所以这里就是执行了委托的方法subscribEvent.printf()
ChangeNum(); /* 事件被触发 */
}else {
Console.WriteLine( "event not fire" );
Console.ReadKey(); /* 回车继续 */
}
}
public EventTest()
{
//构造器一开始就调用了SetValue=>OnNumChanged,所以new就已经调用一次触发了
int n = 5;
SetValue( n );
}
public void SetValue( int n )
{
if ( value != n )
{
value = n;
OnNumChanged();
}
}
}
/***********订阅器类***********/
public class subscribEvent
{
public void printf()
{
Console.WriteLine( "event fire" );
Console.ReadKey(); /* 回车继续 */
}
}
/***********触发***********/
public class MainClass
{
public static void Main()
{
//第一次new的时候构造器就走了一次OnNumChanged方法,输出了一个event not fire
EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
subscribEvent v = new subscribEvent(); /* 实例化对象 */
//这里new了委托都是还没有执行委托,都是把开始是null的ChangeNum 赋值成了一个不是null的变量
//再次执行OnNumChanged方法的时候就不会再event not fire了
e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
//调用SetValue=>OnNumChanged ChangeNum !=null 输出event fire
e.SetValue( 7 );
e.SetValue( 11 );
}
}
}
event not fire
event fire
event fire
C# 泛型(Generic)
在声明泛型方法/泛型类的时候,可以给泛型加上一定的约束来满足我们特定的一些条件。
比如:
using System;
using System.Web.Caching;
namespace Demo.CacheManager
{
public class CacheHelper<T> where T:new()
{
}
}
泛型限定条件:
T:结构(类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型)
T:类 (类型参数必须是引用类型,包括任何类、接口、委托或数组类型)
T:new() (类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时new() 约束必须最后指定)
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。
可以指定多个接口约束。约束接口也可以是泛型的。
T:U
C# 匿名方法
编写匿名方法的语法
匿名方法是通过使用 delegate 关键字创建委托实例来声明的。例如:
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
实例
using System;
delegate void NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static void AddNum(int p)
{
num += p;
Console.WriteLine("Named Method: {0}", num);
}
public static void MultNum(int q)
{
num *= q;
Console.WriteLine("Named Method: {0}", num);
}
static void Main(string[] args)
{
// 使用匿名方法创建委托实例
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
// 使用匿名方法调用委托
nc(10);
// 使用命名方法实例化委托
nc = new NumberChanger(AddNum);
// 使用命名方法调用委托
nc(5);
// 使用另一个命名方法实例化委托
nc = new NumberChanger(MultNum);
// 使用命名方法调用委托
nc(2);
Console.ReadKey();
}
}
}
Anonymous Method: 10
Named Method: 15
Named Method: 30
C# 不安全代码:指针 type*
在vs中是不能正常运行的,需要csc /unsafe prog1.cs
具体百度设置vs
指针变量声明的一般形式为:
type* var-name;
int* p p 是指向整数的指针。
int** p p 是指向整数的指针的指针。
在同一个声明中声明多个指针时,星号 * 仅与基础类型一起写入;而不是用作每个指针名称的前缀。 例如:
int* p1, p2, p3; // 正确
int *p1, *p2, *p3; // 错误 ,不要每个都写一遍
实例
using System;
namespace UnsafeCodeApplication
{
class Program
{
static unsafe void Main(string[] args)
{
int var = 20;
int* p = &var;
Console.WriteLine("Data is: {0} ", var);
Console.WriteLine("Address is: {0}", (int)p);
Console.ReadKey();
}
}
}
Data is: 20
Address is: 99215364
您可以使用 ToString() 方法检索存储在指针变量所引用位置的数据。下面的实例演示了这点:
实例
using System;
namespace UnsafeCodeApplication
{
class Program
{
public static void Main()
{
unsafe
{
int var = 20;
int* p = &var;
Console.WriteLine("Data is: {0} " , var);
Console.WriteLine("Data is: {0} " , p->ToString());
Console.WriteLine("Address is: {0} " , (int)p);
}
Console.ReadKey();
}
}
}
Data is: 20
Data is: 20
Address is: 77128984
您可以向方法传递指针变量作为方法的参数。与ref差不多,引用位置
实例
using System;
namespace UnsafeCodeApplication
{
class TestPointer
{
public unsafe void swap(int* p, int *q)
{
int temp = *p;
*p = *q;
*q = temp;
}
public unsafe static void Main()
{
TestPointer p = new TestPointer();
int var1 = 10;
int var2 = 20;
int* x = &var1;
int* y = &var2;
Console.WriteLine("Before Swap: var1:{0}, var2: {1}", var1, var2);
p.swap(x, y);
Console.WriteLine("After Swap: var1:{0}, var2: {1}", var1, var2);
Console.ReadKey();
}
}
}
Before Swap: var1: 10, var2: 20
After Swap: var1: 20, var2: 10
using System;
namespace UnsafeCodeApplication
{
class TestPointer
{
public unsafe static void Main()
{
int[] list = {10, 100, 200};
fixed(int *ptr = list)
/* 显示指针中数组地址 */
for ( int i = 0; i < 3; i++)
{
Console.WriteLine("Address of list[{0}]={1}",i,(int)(ptr + i));
Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i));
}
Console.ReadKey();
}
}
}
Address of list[0] = 31627168
Value of list[0] = 10
Address of list[1] = 31627172
Value of list[1] = 100
Address of list[2] = 31627176
Value of list[2] = 200
C# 多线程
创建线程
线程是通过扩展 Thread 类创建的。扩展的 Thread 类调用 Start() 方法来开始子线程的执行。
new ThreadStart(执行的线程类);
下面的程序演示了这个概念:
实例
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
In Main: Creating the Child thread
Child thread starts
销毁线程
Abort() 方法用于销毁线程。
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
// 停止主线程一段时间
Thread.Sleep(2000);
// 现在中止子线程
Console.WriteLine("In Main: Aborting the Child thread");
childThread.Abort();
Console.ReadKey();
}