方法
方法在類中用於執行計算或其它行為的成員.聲明格式:
method-header
method-body
其中方法頭method-header的格式:
attributes method-modifiers return-type member-name (formal-parameter-list)
傳遞給方法的參數在方法的形式參數表formal-parameter-list中聲明
在方法的聲明中,至少應包括方法名稱,修飾符和參數類型,返回值和參數名則不是必須的.
注意:方法名member-name不應與同一個類中的其它方法同名,也不能與類中的其它成員名稱
相同.
修飾符
方法的修飾符method-modifier可以是:
new
public
protected
internal
private
static
virtual
sealed
override
abstract
extern
對於使用了atstract和extern修飾符的方法,方法的執行體method-body僅僅只有一個簡單
的分號.其它所有的方法執行體中應包含調用該方法所需要執行的語句.
返回值
方法的返回值的類型可以是合法的C#的數據類型.C#在方法的執行部分通過return語句得到
返回值,如
using System;
class Test
{
public int max(int x,int y)
{
if(x>y)
return x;
else
return y;
}
}
public void Main()
{
Console.WriteLine("the max of 6 and 8 is:{0}",max(6,8));
}
如果在return後不跟隨任何值,方法返回值是void型的.
方法中的參數
C#中方法的參數有四种類型:
值參數,不含任何修飾符.
引用型參數,以ref修飾符聲明.
輸出參數,以out修飾符聲明.
數組型參數,以params修飾符聲明.
值參數
當利用值向方法傳遞參數時,編譯程序給實參的值做一份拷貝,並且將此拷貝傳遞給該方法.
被調用的方法不會修改內存中實參的值,所以使用值參數時,可以保證實際值是安全的.
下面的例子可以說明這個問題
using system;
class Test
{
static void Swap(int x,int y)
{
int temp=x;
x=y;
y=temp;
}
static void Main()
{
int i=1,j=2;
Swap(i,j);
Console.WriteLine("i={0},j={1}",i,j);
}
}
程序輸出的結果是:
i=1,j=2
程序並沒有達到交換的目的.
引用型參數
和值參不同的是,引用型參數並不開辟新的內存區域.當利用引用型參數向方法傳遞形參時,
編譯程序將把實際值在內存中的地址傳遞給方法.
下面的例子有說明這個
using System;
class Test
{
static void Swap(ref int x,ref int y)
{
int temp=x;
x=y;
y=temp;
}
static void Main()
{
int i=1,j=2;
Swap(ref i,ref j);
Console.WriteLine("i={0},j={1}",i,j);
}
}
程序運行的輸出結果是:
i=2,j=1
在方法中使用引用型參數,會經常可能多個變量名指向同一處內存地址.見下面的例子:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsApplication2
{
class A
{
public string s="ssss";
void F(ref string a, ref string b)
{
s = "One";
a = "Two";
b = "Three";
}
public void G()
{
F(ref s, ref s);
}
public void R()
{
Console.WriteLine("S:{0}", s);
}
}
class T
{
public static void Main()
{
A a=new A();
a.G();
a.R();
}
}
}
程序運行的結果是
S:Three
在方法G對F的調用過程中,S的引用被同時傳遞給了a和b.此時,s,a,b同時指向了同一塊內存
區域.
輸出參數
與引用型參數類似,輸出型參數也不開辟新的內存區域,與引用型參數的差別在於,調用方法
前無需對變量進行初始化.輸出型參數用於傳遞方法返回數據.
out修飾符後應跟隨與形參的類型相同的類型聲明.在方法返回後,傳遞的變量被認為經過了
初始化.
using System;
class Test
{
static void SplitPath(string path,out string dir,out string name)
{
int i=path.Length;
while(i>0)
{
char ch=path[i-1];
if(ch=='\\' || ch=='/' || ch==':') break;
i--;
}
dir=path.Substring(0,i);
name=path.Substring(i);
}
static void Main()
{
string dir,name;
SplitPath("c:\\Window\\System\\hello.txt",out dir,out name);
Console.WriteLine(dir);
Console.WriteLine(name);
}
}
程序輸出結果:
c:\Windwo\System\
hello.txt
我們看到,變量dir和name在傳遞給SplitPath之前並未初始化,在調用之後它們則有了明確
的值.
數組型參數
如果形參表中包含了數組型參數,那麼它必須在參數表中位於最後.另外,參數只允許是一維
數組.比如,string[]和string[][]類型都可以作為數組型參數,而string[,]則不能.最後數
組型參數不能再有ref和out修飾符.
using System;
class Test
{
static void F(params int[] args)
{
Console.WriteLine("Array contains {0} elements:",args.Length);
foreach (int i in args) Console.Write("{0},",i);
Console.WriteLine();
}
public static void Main()
{
int[] a={1,2,3};
F(a);
F(10,20,30,40);
F();
}
}
程序運行結果:
Array contains 3 elements:
1,2,3,
Array contains 4 elements:
10,20,30,40,
Array contains 0 elements:
靜態和非靜態的方法
C#的類定義中可以包含兩种方法:靜態的和非靜態的.使用了static修飾符的方法為靜態方
法,反之則是非靜態的.
靜態方法是一种特殊的成員方法,它不屬於類的某一個具體的實例.非靜態方法可以訪問類
中的任何成員,而靜態方法只能訪問類中的靜態成員.
下面這個列子說明這一點.
class A
{
int x;
static int y;
static int F()
{
x=1; //錯誤,不允許訪問
y=2; //正確,允許訪問
}
}
在這個類定議中,靜態方法F()可以訪問類中靜態成員s,但不能訪問非靜態的成員x.這是因
為,x作為非靜態成員,在類的每個實例中都占有一個存儲(或者說有一個副本),而靜態方法
是類所共享的,它無法判斷出當前的x屬於那個類的實例.所以不知道應該到內存的哪個地址
去讀取當前x的值.而y是靜態成員,所有類的實例都公用一個副本,靜態方法F使用它就不存
什麼問題.
是不是靜態方法就無法識別類的實例了呢?答案:不是.
我下面看一下有關windows窗口的例子
using System;
class Window
{
public string m_caption; //窗口的標題
public bool IsActive; //窗口是否被激活
public handle m_handle; //窗口的句柄
public static int m_total;//當前打開的窗口數目
public handle Window()
{
m_total++;
return m_handle;
}
~Window()
{
m_total--;
}
public static string GetWindowCaption(Window w)
{
return w.m_caption;
}
}
分析一下上面例子中的代碼,每個窗口都有窗口標題m_caption,窗口句柄m_handle,窗口是
否激活IsActive三個非靜態的數據成員(窗口句柄是Windows操作系統中保存窗口相關信息
的一种數據結構,我們在這個例子中簡化了對句柄的使用).系統中總共打開窗口的數目
m_total作為一個靜態成員.每個窗口調用構造函數創建,這時候m_total的值加1.窗口關閉
或因為其它行為撤消時,通過析構函數m_total的值減1.我們要注意窗口類的靜態方法
GetWindowCaption(Window w).這里它通過參數w將對像傳遞給方法執行,這樣它就可以通過
具體實例中的類的實例指明調用的對像,這時它可以訪問具體實例中的成員,無論是靜態成
員還是非靜態成員.
方法的重載
類中兩個以上的方法(包括隱藏的繼承而來的方法),取的名字相同,只要使用的參數類型或
者參數個數不同,編譯器便知道在何种情況下應該調用哪個方法,這就叫做方法的重載.
下面看個例子
using System;
class Student
{
public string s_name;
public int s_age;
public float s_weight;
public Student(string n, int a, float w)
{
s_name = n;
s_age = a;
s_weight = w;
}
public int max(int x, int y)
{
if (x > y) return x;
else return y;
}
public float max(float x, float y)
{
if (x > y) return x;
else return y;
}
}
class test
{
public static void Main()
{
Student s1=new Student("Mike",21,70);
Student s2=new Student("John",21,70);
if (s1.max(s1.s_age, s2.s_age) == s1.s_age)
Console.WriteLine("{0} age is bigger than {1}.", s1.s_name, s2.s_name);
else
Console.WriteLine("{0} age is smaller than {1}.", s1.s_name,
s2.s_name);
if (s1.max(s1.s_weight, s2.s_weight) == s1.s_weight)
Console.WriteLine("{0} age is bigger than {1}.", s1.s_name, s2.s_name);
else
Console.WriteLine("{0} age is smaller than {1}.", s1.s_name,
s2.s_name);
}
}
程序運行的結果:
Mike age is bigger than John.
Mike age is bigger than John.
操作符重載
C#中,操作符重載總是在類中進行聲明,並且通過調用類的成員方法來實現.操作作重載的聲
明的格式為:
type operator operator-name (formal-param-list)
C#中,下列操作符都是可以重載的
+ - ! ~ ++ -- TRUE FALSE
* / % | ^ << >> == != > < >= <=
但也有一些操作符是不允許進行重載的,如:
=,&&,||,?:,NEW,TYPEOF,SIZEOF,IS
一元操作符重載,一元操作符重載時操作符只作個用於一個對像,此時參數表為空,當前對像
作為操作符的單操作數.
下面我們舉一個角色類遊戲中經常遇到的例子.扮演的角色有內力,體力,經驗值,剩餘體力,
剩餘內力五個屬性,每當經驗值達到一定程度時,角色便會升級,體力,內力上升,剩餘體力和
內力補滿."升級"我們使用重載操作符"++"來實現.
using System;
class Player
{
public int neili;
public int tili;
public int jingyan;
public int neili_r;
public int tili_r;
public Player()
{
neili = 10;
tili = 50;
jingyan = 0;
neili_r = 50;
tili_r = 50;
}
public static Player operator ++(Player p)
{
p.neili = p.neili + 50;
p.tili = p.tili + 100;
p.neili_r = p.neili;
p.tili_r = p.tili;
return p;
}
public void Show()
{
Console.WriteLine("Tili:{0}", tili);
Console.WriteLine("Jingyan:{0}", jingyan);
Console.WriteLine("Neili:{0}", neili);
Console.WriteLine("Tili_Full:{0}", tili_r);
Console.WriteLine("Neili_Full:{0}", neili_r);
}
}
class Test
{
public static void Main()
{
Player man = new Player();
man.Show();
man++;
Console.WriteLine("Now upgrading...");
man.Show();
}
}
程序輸出結果:
Tili:50
Jingyan:0
Neili:10
Tili_Full:50
Neili_Full:50
Now upgrading...
Tili:150
Jingyan:0
Neili:60
Tili_Full:150
Neili_Full:60
二元操作符重載
大多數情況下我們使用二元操作符重載.這時參數表中有一個參數,當前對像作為該操作符
的左操作數,參數作為操作符的右操作數.
下面是一個二元操作符重載的例子.即笛卡兒坐標相加.
using System;
class DKR
{
public int x, y, z;
public DKR(int vx, int vy, int vz)
{
x = vx;
y = vy;
z = vz;
}
public static DKR operator +(DKR d1, DKR d2)
{
DKR dkr = new DKR(0, 0, 0);
dkr.x = d1.x + d2.x;
dkr.y = d1.y + d2.y;
dkr.z = d1.z + d2.z;
return dkr;
}
}
class Test
{
public static void Main()
{
DKR d1 = new DKR(3,2,1);
DKR d2 = new DKR(0, 6, 5);
DKR d3 = d1 + d2;
Console.WriteLine("The 3d location of d3 is:{0},{1},{2}",d3.x,d3.y,d3.z);
}
}
程序輸出結果:
The 3d location of d3 is:3,8,6