1. 什么是局部类型?
C# 2.0 引入了局部类型的概念。局部类型允许我们将一个类、结构或接口分成几个部分,分别实现在几个不同的.cs文件中。
局部类型适用于以下情况:
(1) 类型特别大,不宜放在一个文件中实现。
(2) 一个类型中的一部分代码为自动化工具生成的代码,不宜与我们自己编写的代码混合在一起。
(3) 需要多人合作编写一个类。
局部类型是一个纯语言层的编译处理,不影响任何执行机制——事实上C#编译器在编译的时候仍会将各个部分的局部类型合并成一个完整的类。
public partial class Program
{
static void Main(string[] args)
{
}
}
partial class Program
{
public void Test()
{
}
}
2. 局部类型的限制
(1) 局部类型只适用于类、接口、结构,不支持委托和枚举。
(2) 同一个类型的各个部分必须都有修饰符 partial。
(3) 使用局部类型时,一个类型的各个部分必须位于相同的命名空间中。
(4) 一个类型的各个部分必须被同时编译。
3. 局部类型的注意点
(1) 关键字partial是一个上下文关键字,只有和 class、struct、interface 放在一起时才有关键字的含义。因此partial的引入不会影响现有代码中名称为partial的变量。
(2) 局部类型的各个部分一般是分开放在几个不同的.cs文件中,但C#编译器允许我们将他们放在同一文件中。 4. 局部类型的应用特性
在局部类型上的特性具有“累加”效应。
[Attribute1, Attribute2("Hello")]
partial class Class1
{
}
[Attribute3, Attribute2("Exit")]
partial class Class1
{
}
相当于
[Attribute1, Attribute2("Hello"), Attribute3, Attribute2("Exit")]
class Class1
{
}
注:Attribute2属性允许在类上多次使用。
5. 局部类型上的修饰符
(1) 一个类型的各个部分上的访问修饰符必须维持一致性。
(2) 如果一个类型有一个部分使用了abstract修饰符,那么整个类都将被视为抽象类。
(3) 如果一个类型有一个部分使用了 sealed 修饰符,那么整个类都将被视为密封类。
(4) 一个类的各个部分不能使用相互矛盾的修饰符,比如不能在一个部分上使用abstract,又在另一个部分上使用sealed。
6. 局部类型的基类和接口
(1) 一个类型的各个部分上指定的基类必须一致。某个部分可以不指定基类,但如果指定,则必须相同。
(2) 局部类型上的接口具有“累加”效应。
partial class Class2: Iinterface1, Iinterface2 {}
partial class Class2: Iinterface3 {}
partial class Class2: Iinterface2 {}
相当于
class Class2: Iinterface1, Iinterface2, Iinterface3 {}
posted @
2008-09-10 16:37 天书 阅读(167) |
评论 (0) |
编辑 收藏
从VS2005开始就不允许非创建此控件的线程来调用它,那么解决方法是在该控件所在窗体里加上一句话:
CheckForIllegalCrossThreadCalls = false;
private void Form1_Load(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false;
//信号量
signel sgl = new signel(10);
//新建一个队列缓冲区
Queue<int> que = new Queue<int>(10);
Reader readerThread = new Reader(sgl,que,this);
Writer writerThread = new Writer(sgl,que,this);
//开启两个线程
Thread tRead = new Thread(readerThread.Process);
Thread tWrite= new Thread(writerThread.Process);
tRead.Start();
tWrite.Start();
}
posted @
2008-09-10 13:14 天书 阅读(1240) |
评论 (0) |
编辑 收藏
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Collections;
namespace MulThead
{
class signel
{
public Semaphore singleRead;
public Semaphore singleWrite;
public Semaphore singleMutex;
private int size;
public signel(int size)
{
this.size = size;
singleRead = new Semaphore(0,size); //一共size个read资源,目前有0个可用
singleWrite = new Semaphore(size, size); //一共size个write资源,目前有size个可用
singleMutex = new Semaphore(1, 1);
}
}
class Reader
{
private signel sgl;
private Queue<int> que;
private Form1 f1;
//保存信号量和队列的一个引用
public Reader(signel sgl, Queue<int> que,Form1 f1)
{
this.sgl = sgl;
this.que = que;
this.f1 = f1;
}
public void Process()
{
int readvalue;
while(true)
{
sgl.singleRead.WaitOne();
sgl.singleMutex.WaitOne();
readvalue = que.Dequeue();
f1.txtReader.Text += readvalue.ToString();
f1.txtReader.Text += " ";
sgl.singleMutex.Release();
sgl.singleWrite.Release(); //释放写资源
}
}
}
class Writer
{
private signel sgl;
private Queue<int> que;
private Form1 f1;
public Writer(signel sgl, Queue<int> que,Form1 f1)
{
this.sgl = sgl;
this.que = que;
this.f1 = f1;
}
public void Process()
{
int i = 0;
while(i < 50)
{
i++;
sgl.singleWrite.WaitOne();
sgl.singleMutex.WaitOne();
f1.txtWriter.Text += i.ToString();
f1.txtWriter.Text += " ";
que.Enqueue(i);
sgl.singleMutex.Release();
sgl.singleRead.Release(); //释放读资源
}
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false; //这样在不是创建该控件(此form里的控件)的线程里也可以用这个控件了
//信号量
signel sgl = new signel(10);
//新建一个队列缓冲区
Queue<int> que = new Queue<int>(10);
Reader readerThread = new Reader(sgl,que,this);
Writer writerThread = new Writer(sgl,que,this);
//开启两个线程
Thread tRead = new Thread(readerThread.Process);
Thread tWrite= new Thread(writerThread.Process);
tRead.Start();
tWrite.Start();
}
posted @
2008-09-10 13:07 天书 阅读(555) |
评论 (0) |
编辑 收藏
*******ref 有进有出,out只出不进*************************************************************
Ref指明了方法中使用的是引用型参数,引用型参数不开辟新的内存区域。当利用引用型参数向方法传递形参时,编译程序将把实际值在内存中的地址传递该方法。引用型参数通常已经初始化。
如
int i=1,j=2; //初始化在函数外面 Swap(
ref i,
ref j);
Out输出型参数也不开辟新的内存区域,但与引用型参数不同的是,调用方法之前无需对变量进行初始化,输出型参数主要用于传递方法返回的数据。
string name,path; //没有初始化,初始化在函数里面 File(out name,out path);
学过C/C++的人,对C#的关键字Ref和Out应该都很好理解。它们都提供了一种可以在被调用函数内修改传递的参数的值的方法。因为这一功能很类似C/C++的指针。对于没学过C/C++的,也应该可以明白这两个参数的作用。
虽然Ref和Out都提供了修改参数值的方法,但它们还是有一点点小的区别。
1、Ref在作为参数调用函数之前,变量一定要赋值,否则会得到一个常规编译错误:使用了未赋值的变量。
2、在被调用函数内,以Ref引入的参数在返回前不必为它赋值。
3、Out在作为参数调用函数之前,变量可以不被赋值。
4、在被调用函数内,以Out引入的参数在返回前一定要至少赋值一次。其实本质上讲,Ref更适合理解为给被调用函数传递了一个与原参考同地址的变量。而Out则可以理解为在调用函数前,先给变量找个地方,让被调用函数在给定地点放一个值。
posted @
2008-09-10 10:01 天书 阅读(597) |
评论 (0) |
编辑 收藏
《Effective C#》:值类型和引用类型
在C#中有两种类型的数据,一种是值类型数据,一种是引用类型数据。在编码的时候区分这两种类型数据,可以避免一些细小的编码错误。
首先说说什么类型是值类型,例如:int、float、bool之类的基础类型,以及用struct定义的类型,如:DateTime。除此外,如string,数组,以及用class定义的类型等都是引用类型。对于C#来说,很难罗列出所有类型进行一一分别,这需要自己在编码过程中进行分析总结。
为了更好地说明两种类型之间的区别,借用如下的表格来说明。
|
值类型 |
引用类型 |
内存分配地点 |
分配在栈中 |
分配在堆中 |
效率 |
效率高,不需要地址转换 |
效率低,需要进行地址转换 |
内存回收 |
使用完后,立即回收 |
使用完后,不是立即回收,等待GC回收 |
赋值操作 |
进行复制,创建一个同值新对象 |
只是对原有对象的引用 |
函数参数与返回值 |
是对象的复制 |
是原有对象的引用,并不产生新的对象 |
类型扩展 |
不易扩展 |
容易扩展,方便与类型扩展 |
通过如上细致对比,大家对于值类型和引用类型有个清楚的概念。
不过,无论是对于值类型还是引用类型来说,对于其作为函数参数或者返回值的时候,都是容易犯错误的地方。
对于值类型来说,当其作为函数参数的时候,希望在函数中被修改,那么直接如下操作是不能被修改的。
public void Increment( int i ) { i++; } |
要想在函数中对传进去的参数做真正的修改,需要借助于ref这个关键字,那么正确的形式如下。
public void Increment( ref int i )
|
也就是说,如果需要在函数中对值类型参数进行修改,需要用ref或者out进行标识才能真正实现。
而对于引用类型来说,当其作为函数参数的时候,它所遇到的情况恰恰与值类型相反,即不希望在函数中被修改,举例如下。
public void AddValue( MyType typValue ) { typValue.Count = typValue.Count + 15; } |
由于对于引用类型对象来说,其的赋值操作只是对原有对象的引用,因此在函数对其修改,实际上是直接修改了原有对象数据,这是很多情况不希望发生的(这里例如对数组或者DataTable操作这类)。
为了防止这种事发生,需要给此类型提供clone函数。例如对于如上的类型,可以入下实现。
public class MyType:ICloneable { private int nCount = 0; public int Count { set{ nCount = value;} get{ return nCount;} } public MyType() {} public MyType( int Value) { nCount = Value; }
#region ICloneable Members
public object Clone() { // TODO: Add MyType.Clone implementation return new MyType( nCount ); }
#endregion }
|
那么在调用的时候,用当前的对象的clone作为参数即可。
不过对于引用类型来说,提供一个clone函数不是一件容易的事情,尤其出现引用类型嵌套的时候,所以说去实现一个完全clone功能是件很费事又不讨好的活,这也就是在论坛中常说的深copy和浅copy的问题。话虽如此,如果对于前面所说的有个大概了解,相信实现也不是不可能。
在C#中,尤其自己定义类型的时候,常常由于是用struct来定义还是用class来定义,即是定义一个值类型还是一个引用类型呢。在这本书上给了几个判定条件,如果如下几点都满足的话,建议用struct来定义为值类型,否则用class定义为引用类型。
<!--[if !supportLists]-->1. <!--[endif]-->这个类型是否主要为了数据存储; <!--[if !supportLists]-->2. <!--[endif]-->是否只通过属性来访问对象的数据成员; <!--[if !supportLists]-->3. <!--[endif]-->这个类型是否不会有子类型; <!--[if !supportLists]-->4. <!--[endif]-->在程序处理的时候不会把这个类型对象通过多态来处理。 |
posted @
2008-09-10 09:43 天书 阅读(176) |
评论 (0) |
编辑 收藏
首先:窗体的两个属性 AcceptButton ,和 CancelButton 分别设置为窗体上“确定”和“取消”按钮的名字:bnok 和 bncancel.
其次:确定按钮和取消按钮的"DialogResult"属性分别设置为:ok , cancel
然后:窗体上的控件textbox要在类外部读出来那么设置其 modifier属性为public即可
接着:
int errCount = 0;
while (errCount < 3)
{
FrmLogin flog = new frmLogin();
flog.ShowDialog(this);
if (flog.DialogResult == DialogResult.OK)
{
flog.Username = flog.txtusername.Text;
flog.Password = flog.txtpassword.Text;
if (flog.Username == "liumu" &&
flog.Password == "198331622")
{
MessageBox.Show("欢迎访问该系统", "登录成功");
ShowWelcomePage();
break;
}
else
{
errCount++;
MessageBox.Show("用户名或密码有误请核对后重新输入", "错误提示");
}
}
else if (flog.ShowDialog() == DialogResult.Cancel)
{
Application.Exit();
}
}
if (errCount == 3)
{
MessageBox.Show("您三次输入有误,被迫推出系统,Sorry!", "退出提示");
Application.Exit();
}
posted @
2008-09-09 13:23 天书 阅读(2165) |
评论 (0) |
编辑 收藏
1:对象在类内部实例化且仅一次
2:构造函数私有
3:用属性get将对象读出来,如果为空那么实例化一次如:
public class Singleton
{
static Singleton instance = null;
private Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
posted @
2008-09-08 16:58 天书 阅读(85) |
评论 (0) |
编辑 收藏
public class File
{
string fileName;
public File(string fileName)
{
this.fileName=fileName;
}
public string ReadFile()
{
try
{
StreamReader sr=new StreamReader(fileName,Encoding.Default);
string result=sr.ReadToEnd();
sr.Close();
return result;
}
catch(Exception e){MessageBox.Show(e.Message);}
return null;
}
public void WriteFile(string str)
{
try
{
StreamWriter sw=new StreamWriter(fileName,false,Encoding.Default);
sw.Write(str);
sw.Close();
}
catch(Exception e){MessageBox.Show(e.Message,"保存文件出错!");}
}
}
posted @
2008-09-05 14:26 天书 阅读(524) |
评论 (0) |
编辑 收藏
private static void SetTreeViewIcon(TreeView tv, NodeType firstType, NodeType secondType, NodeType thirdType)
{
TreeNode tnRoot = tv.Nodes[0];
tnRoot.ImageIndex = 0;
foreach (TreeNode tnFirst in tnRoot.Nodes)
{
tnFirst.ImageIndex = tnFirst.SelectedImageIndex = (int)firstType;
foreach (TreeNode tnSecond in tnFirst.Nodes)
{
tnSecond.ImageIndex = tnSecond.SelectedImageIndex = (int)secondType;
foreach (TreeNode tnThird in tnSecond.Nodes)
{
tnThird.ImageIndex = tnThird.SelectedImageIndex = (int)thirdType;
}
}
}
}
posted @
2008-09-04 20:43 天书 阅读(505) |
评论 (0) |
编辑 收藏
首先在form窗体上拖上一个
ImageList控件,在属性窗口中设置它的Image属性即导入几个图标,图标都是有索引的。接着在程序中写上一句代码:
myNeTree.ImageList = NeImgList; myNeTree为你在程序中事先定义好的如:private TreeView myNeTree;最后在写树节点的时候设置
ImageIndex 属性 :
typenode.ImageIndex = 1;要想让节点选上时变图标要设置节点的SelectedImageIndex属性typenode.SelectedImageIndex = 3; 像1,3都是ImageList中图标的索引值
posted @
2008-09-04 16:16 天书 阅读(7805) |
评论 (0) |
编辑 收藏