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 天書 閱讀(176) |
評論 (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 天書 閱讀(1265) |
評論 (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 天書 閱讀(562) |
評論 (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 天書 閱讀(612) |
評論 (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 天書 閱讀(181) |
評論 (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 天書 閱讀(2175) |
評論 (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 天書 閱讀(92) |
評論 (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 天書 閱讀(530) |
評論 (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 天書 閱讀(512) |
評論 (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 天書 閱讀(7852) |
評論 (0) |
編輯 收藏