推荐阅读:
- 我的CSDN
- 我的博客园
- QQ群:704621321
用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。
Net 框架提供了三种预定义特性:
AttributeUsage
Conditional
Obsolete
反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
反射(Reflection)有下列用途:
它允许在运行时查看特性(attribute)信息。
它允许审查集合中的各种类型,以及实例化这些类型。
它允许延迟绑定的方法和属性(property)。
它允许在运行时创建新类型,然后使用这些类型执行一些任务。
使用 访问器(accessors) 让私有域的值可被读写或操作。例如:有一个名为 Student 的类,带有 age私有域。我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。
代码实现如下:
using System;
namespace 属性
{
class Student
{
private int age = 0;
// 声明类型为 int 的 Age 属性
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
}
class ExampleDemo
{
public static void Main()
{
// 创建一个新的 Student 对象
Student s = new Student();
s.Age = 9; //9
// 增加年龄
s.Age += 1; //10
Console.ReadKey();
}
}
}
允许一个对象可以像数组一样被索引。当为类定义一个索引器时,该类的行为就会像一个虚拟数组(virtual array) 一样。可以使用数组访问运算符 [ ] 来访问该类的实例。
格式如下:
//索引器定义的时候带有 this 关键字,它指向对象实例
element-type this[int index]
{
// get 访问器
get
{
// 返回 index 指定的值
}
// set 访问器
set
{
// 设置 index 指定的值
}
}
下面举个例子来看看索引器如何使用:
using System;
namespace 索引器
{
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.";
}
public string this[int index]
{
//取值,输出时用到
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
return ( tmp );
}
//设置值,重置值得时候使用
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
names[0] = "被重置后的值";
for ( int i = 0; i < IndexedNames.size; i++ )
{
Console.WriteLine(names[i]);
}
Console.ReadKey();
}
}
}
上面代码在这里就不做过多的解释了,注释已经在代码中标注。
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。派生自 System.Delegate 类。
格式:
[访问修饰符] delegate <return type> <delegate-name> <parameter list>
例如:
public delegate int MyDelegate (string s);
使用方法:
using System;
delegate int NumberChanger(int n);
namespace 委托
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
// 使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
委托的多播:委托对象可使用 “+” 运算符进行合并。只有相同类型的委托可被合并。
例如:
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 nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1; //15
nc += nc2; //15*5=75
// 调用多播
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
下面再举一个委托多播实例:例如小明叫小张买完车票,之后接着又让他带张电影票
// 小张类
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();
}
}
事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。事件使用 发布-订阅(publisher-subscriber) 模型。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
通过事件使用委托:在类的内部声明事件,首先必须声明该事件的委托类型。然后,声明事件本身,使用 event 关键字。
例如:声明一个myDelegate委托,然后声明一个BoilerEventLog事件
//声明委托
public delegate void myDelegate();
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
例如:在命名空间“通过事件使用委托”中,创建EventTest类作为发布器,并声明myDelegate委托和ChangeNum事件;创建subscribEvent类作为订阅器类;创建MainClass类,注册事件。
using System;
namespace 通过事件使用委托
{
/***********发布器类***********/
public class EventTest
{
private int value;
//委托
public delegate void myDelegate();
//事件
public event myDelegate ChangeNum;
//构造函数
public EventTest()
{
int n = 5;
SetValue( n );
}
protected void OnNumChanged()
{
if ( ChangeNum != null )
{
//有事件
ChangeNum(); /* 事件被触发 */
}else {
//没有事件
Console.WriteLine( "event not fire" );
Console.ReadKey(); /* 回车继续 */
}
}
//设置值,并触发事件
public void SetValue( int n )
{
if ( value != n )
{
value = n;
OnNumChanged();
}else{
Console.WriteLine( "值相等" );
Console.ReadKey(); /* 回车继续 */}
}
}
/***********订阅器类***********/
public class subscribEvent
{
public void printf()
{
Console.WriteLine( "event fire" );
Console.ReadKey(); /* 回车继续 */
}
}
/***********触发***********/
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件(事件为空) */
subscribEvent v = new subscribEvent(); /* 实例化订阅器 */
e.ChangeNum += new EventTest.myDelegate( v.printf ); /* 注册事件 */
e.SetValue( 7 );
e.SetValue( 10 );
}
}
}
在 C# 中,集合继承自System.Collection;在 C# 中,Object 类是所有数据类型的基类
1.动态数组(ArrayList):可以替代一个数组。与数组不同的是,可以使用索引在指定的位置添加和移除。
ArrayList al = new ArrayList();
al.Add(45);
2.哈希表(Hashtable)存储:键/值对,使用键来访问集合中的元素。
Hashtable ht = new Hashtable();
ht.Add("001", "小木游戏");
3.排序列表(SortedList):集合中的各项总是按键值排序排序,是数组和哈希表的组合,存储键/值对,使用键和索引来访问集合中的元素。如果使用索引访问各项,则它是一个动态数组(ArrayList),如果使用键访问各项,则它是一个哈希表(Hashtable)。
SortedList sl = new SortedList();
sl.Add("004", "小木游戏");
sl.Add("001", "小木子");
// 获取键的集合
ICollection key = sl.Keys;
foreach (string k in key)
{
Console.WriteLine(k + ": " + sl[k]);
}
输出结果:
001: 小木子
002: 小木游戏
4.堆栈(Stack):后进先出
Stack st = new Stack();
st.Push('小木');
st.Push('游戏');
st.Pop();//游戏
st.Pop();//小木
5.队列(Queue):先进先出
Queue q = new Queue();
q.Enqueue('小木');
q.Enqueue('游戏');
char ch1 = (char)q.Dequeue();//小木
char ch2 = (char)q.Dequeue();//游戏
6.点阵列(BitArray):使用布尔值来表示,其中 true 表示位是开启的(1),false 表示位是关闭的(0)。当需要存储位,但是事先不知道位数时,则使用点阵列
// 创建两1个大小为 8 的点阵列
BitArray ba1 = new BitArray(8);
byte[] a = { 60 };
for (int i = 0; i < ba1.Count; i++)
{
Console.Write("{0, -6} ", ba1[i]);//False False True True True True False False
}
上面输出结果为将60的二进制数,转换成对应的布尔值。
在封装公共组件的时候,很多时候我们的类/方法不需要关注调用者传递的实体是"什么",这个时候就可以使用泛型。
泛型方法:
//交换函数
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a = 10;
int b = 20;
char c = 'I';
char d = 'V';
// 调用 swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
Console.ReadKey();
}
泛型委托:
delegate T NumberChanger<T>(T n);
// 创建委托实例
NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
匿名方法是通过使用 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);
}
static void Main(string[] args)
{
///////////////方法一
// 使用匿名方法创建委托实例
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
// 使用匿名方法调用委托
nc(10);
//////////////方法二
// 使用命名方法实例化委托
nc = new NumberChanger(AddNum);
// 使用命名方法调用委托
nc(5);
Console.ReadKey();
}
}
}
不安全代码或非托管代码是指使用了指针变量的代码块。当一个代码块使用 unsafe 修饰符标记时,C# 允许在函数中使用指针变量
例如:在函数返回类型前加上关键字unsafe ,函数中便可以使用指针。
public unsafe void swap(int* p, int *q)
{
int temp = *p;
*p = *q;
*q = temp;
}
线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。为了同时执行多个任务,它可以被划分为更小的线程。
线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。
线程生命周期中的各种状态:
1.未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
2.就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
3.不可运行状态:下面的几种情况下线程是不可运行的:
已经调用 Sleep 方法
已经调用 Wait 方法
通过 I/O 操作阻塞
4.死亡状态:当线程已完成执行或已中止时的状况。
进程中第一个被执行的线程称为主线程。当 C# 程序开始执行时,主线程自动创建。使用 Thread 类的 CurrentThread 属性访问线程。
1.创建线程用Thread :
Thread childThread = new Thread(“主线程名”);
注意:这种方法是创建子线程,因为主线程在程序开始时自动被创建
2.暂停线程用 sleep() 方法:
Thread.Sleep(暂停时间);
3.销毁线程用Abort()方法:
线程名.Abort();