address:http://wenku.baidu.com/link?url=bDFZzy7VFKi8UzOVkoIMVdyRVcFsBhOiU78Qo34E78xHn8JKKKBc_nNIG-WUZVOm2rsja70zZteZohZtq3D_S33XCGz3FUzi_ae6hDup2DC
usingSystem;
class Person//类的定义,class是保留字,表示定义一个类,Person是类名
{
private string name="张三"; //类的数据成员声明
private int age=12; //private表示私有数据成员
public void Display() //类的方法(函数)声明,显示姓名和年龄
{
Console.WriteLine("姓名:{0},年龄:{1}",name,age);
}
public void SetName(string PersonName)//修改姓名的方法(函数)
{
name=PersonName;
}
public void SetAge(int PersonAge)
{
age=PersonAge;
}
}
一. 类的构造函数
a。类在new出一个实例的时候会自动调用构造函数。
b。一些类在创建的时候需要初始化工作,这个工作可以由构造函数完成。
c。构造函数和类名称相同,没有返回值
public Person(string name,int age)
{
Name=name;
Age=age;
}
当用:
Person OnePerson=new Person(“张五”,20) 语句生 Person 类对象时,将自动调用以上构造函数.
请注意如何把参数传递给构造函数。
二.类的析构函数
a。变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销。类的对象被撤销时,将自动调用析构函数。
b。一些善后工作可放在析构函数中完成。
C。析构函数的名称 “~类名” ,无返回类型 也无参数。
~Person()
{
}
C#中类析构函数不能显示地被调用,它是被垃圾收集器撤销不被使用的对象时,进行自动调用的。
三.类的构造函数的重载
在C#语言中,同一个类中函数名称相同,参数类型不同或者参数个数不同的函数,这叫函数重载。
仅仅返回值不同不能看做不同的函数。
usingSystem;
namespace e1//定义以下代码所属命名空间,意义见以后章节
{
class Person
{
private String name="张三";//类的数据成员声明
private int age=12;
public void Display()//类的方法(函数)声明,显示姓名和年龄
{
Console.WriteLine("姓名:{0},年龄:{1}",name,age);
}
public void SetName(string PersonName)//指定修改姓名的方法(函数)
{
name=PersonName;
}
public void SetAge(int PersonAge)//指定修改年龄的方法(函数)
{
age=PersonAge;
}
public Person (string Name,int Age)//构造函数,函数名和类同名,无返回值
{
name=Name;
age=Age;
}
public Person()//类的构造函数重载
{
name="田七";
age=12;
}
}
class Class1
{
static void Main(string[] args)
{
Person OnePerson=new Person("李四",30);//生成类的对象
OnePerson.Display();
//下句错误,在其它类(Class1类)中,不能直接修改Person类中的私有成员。
//OnePerson.name="王五";
//只能通过Person类中公有方法SetName修改Person类中的私有成员name。
OnePerson.SetName("王五");
OnePerson.SetAge(40);
OnePerson.Display();
OnePerson=new Person();
OnePerson.Display();
}
}
}
键入CTRL+F5运行后,显示的效果是:
姓名: 李四,年龄:30
姓名: 王五,年龄:40
姓名: 田七,年龄:12
四. C#的数据类型
可分为三种类型:值类型,应用类型,指针类型(仅仅用于非安全代码中)
五.测试运算符is
using System;
class Test
{
public static void Main()
{
Console.WriteLine(1 is int);
Console.WriteLine(1 is float);
Console.WriteLine(1.0f is float);
Console.WriteLine(1.0d is double);
}
}
输出为:
True
False
True
True
六. 溢出检查操作符 checked 和 unchecked
如例:在进行整型算术运算(如+、-、*、/等)或从一种整型显式转换到另一种整型时,有可能出现运算结果超出这个结果所属类型值域的情况
类似这样的情况我们称之为溢出。
整型算术运算表达式可以用checked或unchecked溢出检查操作符,决定在编译和运行时是否对表达式溢出进行检查。
如果表达式不使用溢出检查操作符或使用了checked操作符,常量表达式溢出,在编译时将产生错误,表达式中包含变量,程序运行时执行该表达式产生溢出,将产生异常提示信息。
而使用了unchecked操作符的表达式语句,即使表达式产生溢出,编译和运行时都不会产生错误提示。但这往往会出现一些不可预期的结果,所以使用unchecked操作符要小心。
such as:
using system;
class Class1
{
static void Mian(string[] args)
{
const int x=int.MaxValue;
checked //check overflow
{
int y=x;
Console.WriteLine("y eque result is {0}",y);
}
unchecked //don't check overflow
{
int z=x*3;
Console.WriteLine("z value is {0}",z);
}
}
}
七.类的继承
派生类的声明格式如下:
属性 类修饰符 class 派生类名:基类名
{类体}
八.Base 关键字
base关键字用于从派生类中访问基类成员
1. 在定义派生类的构造函数中,指明要调用的基类构造函数,由于基类可能有多个构造函数,根据base后的参数类型和个数,指明要调用哪一个基类构造函数。
2. 在派生类的方法中调用基类中被派生类覆盖的方法
九.覆盖基类成员
在派生类中,通过声明与基类完全相同新成员,可以覆盖基类的同名成员,完全相同是指函数类型、函数名、参数类型和个数都相同
如下例中的方法Display()。派生类覆盖基类成员不算错误,但会导致编译器发出警告。如果增加new修饰符,表示认可覆盖,编译器不再发出警告。
请注意,覆盖基类的同名成员,并不是移走基类成员,只是必须用如下格式访问基类中被派生类覆盖的方法:base.Display()。
十.base和this用法(着重说明base关键字)
-------------------------------------------------------------------------------------------------------------------------------------
base关键字用于从派生类中访问基类成员,它有两种基本用法:
1. 在定义派生类的构造函数中,指明要调用的基类构造函数,由于基类可能有多个构造函数,根据base后的参数类型和个数,指明要调用哪一个基类构造函数。
2. 在派生类的方法中调用基类中被派生类覆盖的方法时.使用base关键字
-------------------------------------------------------------------------------------------------------------------------------------
base是子类中引用父类
1.base 是为实现太多而设计的
2.this 和 base关键字只指定一个构造函数,也就是说不能同时将base和this作用在一个构造函数上。
3.简单来说,base用于在派生类中访问重写的基类成员;
而this用于访问本类的成员,当然也可包括继承而来的共有的和保护成员
another:
除了base,访问基类成员的另一种方式是: 显式的类型转换来实现,只是该方法不能为静态的方法。
such as:
class Employee:Person //Person类是基类
{
private string department;//部门,新增数据成员
private decimal salary;//薪金,新增数据成员
public employee(string name,int age,string d,decimal s):base(name,age)
{
//注意base的第一种用法,根据参数调用指定基类构造函数,注意参数的传递
department=D;
salary=S;
}
public new void Display()//覆盖基类Display()方法,注意new,不可用override
{
base.Display();//访问基类被覆盖的方法,base的第二种用法
Console.WriteLine("部门:{0}薪金:{1}",department,salary);
}
}
修改主函数如下:
class Class1
{
staticvoidMain(string[]args)
{
Employee OneEmployee=new Employee("李四",30,"计算机系",2000);
OneEmployee.Display();
}
}
十一.C#语言类继承有如下特点
1. C#语言只允许单继承,即派生类只能有一个基类。
2. C#语言继承是可以传递的,如果C从B派生,B从A派生,那么C不但继承B的成员,还要继承A中的成员.
3. 派生类可以添加新成员,但不能删除基类中的成员。
4. 派生类不能继承基类的构造函数、析构函数和事件。但能继承基类的属性。
5. 派生类可以覆盖基类的同名成员,如果在派生类中覆盖了基类同名成员,基类该成员在派生类中就不能被直接访问,只能通过base.基类方法名访问。
6. 派生类对象也是其基类的对象,但基类对象却不是其派生类的对象。
十二.
在类中常量定义好后,在方法或者主函数调用时不能进行修改。
get和set方法对属性进行读写,是的类实例能直接对私有变量进行修改。
class csharp
{
private string mynames = "jinhua";
private int ages = 100;
public void setname(string trs)
{
mynames=trs;
}
public void setage(int aes)
{
ages=aes;
}
public string name
{
get { return mynames; }
set { mynames = value; }
}
public int age
{
get { return age; }
set { ages = value; }
}
public csharp()
{
Console.WriteLine("name is {0},age is {1}", mynames, ages);
}
public csharp(string myname, int age)
{
mynames = myname;
ages = age;
}
public void print()
{
Console.WriteLine("name is {0},age is {1}", mynames, ages);
}
static void Main(string[] args)
{
csharp cp = new csharp("qujinhua",1006);
cp.name = "yingxin";
cp.age = 1008;
cp.print();
csharp cp2 = new csharp();
cp2.setname("asd");
cp2.setage(123);
cp2.print();
Console.ReadLine();
}
}
十三.静态方法和实例方法
1.静态方法
a.使用static修饰符的方法是静态方法,不是用static修饰符的是实例方法。
b.不论类是否生成对象与否,静态方法都可以被使用,格式如下:
类名.静态方法名称
c.静态方法只能"调用或者使用"该静态方法所在类的“静态数据成员”和“静态方法"
2.函数重载
在同一个类中函数名称相同,函数参数类型或参数个数不同,认为是不相同的函数;仅返回值不同,不能看作不同的函数,这叫做函数的重载。
十四.类的多态性
C sharp语言中支持两种类型的多态性
1.第一种是编译时的多态性:一个类的对象调用多个同名函数,在编译时根据函数的参数类型或参数的个数确定调用哪一个同名函数,而实现何种操作。
(一句话,编译时,一个类对象调用多个同名函数,以实现其某种操作。通过函数重载实现。)
编译时的多态性是通过函数的重载实现的。
2.第二种是运行时的多态性:系统运行时,不同对象调用一个名字相同,参数类型和参数个数完全一样的函数,会完成不同的操作。
(一句话:系统运行时,多个对象调用一个完全相同的函数,完成不同的操作。通过虚函数实现。)
运行时的多态时通过C#的”虚方法实现“
在类方法声明前加 "Virtual"修饰符,即这就是虚方法
3. 备注:“覆盖基类函数成员”
3.1 派生类覆盖基类中的函数成员(完全相同,函数类型,参数个数,参数类型,函数名称)在基类中声明必须使用new关键字,不然编译不通过。
using System;
class A
{
public void Display()//非虚方法
{
Console.Write("非虚方法");
}
public virtual void Display_virtual()//虚方法
{
Console.Write("虚方法");
}
}
class B:A
{
new public void Display()
{
Console.Write("非虚方法的覆盖");
}
public ovreride void Display_virtual()
{
Console.Write("虚方法的覆盖");
}
}
a. “覆盖基类的同名非虚方法,注意使用new“
new public void Display()
{......}
b. 覆盖基类的同名虚方法(虚函数),注意使用override
public ovreride void Display_virtual()
{......}
4. 备注:“解释virtual方法的使用”
class A
{
public virtual void PrintFields()
{
Console .WriteLine ("我是A的方法");
}
}
class B:A
{
public override void PrintFields()
{
Console.WriteLine("我是B的方法");
}
}
class C:A
{
}
class Test
{
static void Main(string[] args)
{
A a;
a = new B();
a.PrintFields();//我是B的方法
a = new C();
a.PrintFields();//我是A的方法
}
}
十五.抽象类和抽象方法
abstract修饰符可以和类、方法、属性、索引器及事件一起使用,在类声明中使用abstract修饰符以表明这个类只能是其他类的基类。
A.抽象类的特性
(1)抽象类不能被实例化
(2)抽象类可以包含抽象方法和抽象访问器
(3)不能用sealed修饰符修改抽象类,因为抽象类本身就是用来给其他类继承的
(4)抽象类的非抽象子类必须实现其继承的所有抽象方法和抽象访问器
B.抽象方法
(1)抽象方法是隐式的虚方法
(2)抽象方法只允许声明在抽象类中
(3)抽象方法不能提供实际的实现,所以没有方法体;抽象方法的实现是在非抽象的派生类中以override重写实现的
(4)抽象方法声明中不可以使用static或者virtual修饰符
(5)abstract关键字不能修饰静态方法或静态属性
C.抽象类的构造函数
(1)不要再抽象类中定义public或protected internal访问权限的构造函数
(2)应在抽象类中定义protected或private访问权限的构造函数
(3)如果在抽象类中定义一个protected构造函数,则在实例化派生类时,基类可以执行初始化任务
D.抽象方法和虚方法的区别
虚方法有实现部分,并且派生类对其重写是可选的;抽象方法没有实现部分,并且强制非抽象派生类对其重写
using System;
using System.Collections;
/抽象类
public abstract class Animal
{
protected string face;
//声明为protected的构造函数,在实例化派生类时,基类可以执行初始化工作
protected Animal() { this.face = "^_^"; /*抽象类构造函数初始化的例子*/}
public abstract void eat();
public abstract string Face
{
get;
}
}
public class dog : Animal
{
//重写基类中的抽象访问器Face
public override string Face
{
get
{
return face;
}
}
//重写基类中的抽象方法eat
public override void eat()
{
Console.WriteLine("狗吃骨头 " + Face);
}
}
public class cat : Animal
{
public override string Face
{
get
{
return face;
}
}
public override void eat()
{
Console.WriteLine("猫吃鱼 " + Face);
}
}
public class panda : Animal
{
public override string Face
{
get
{
return face;
}
}
public override void eat()
{
Console.WriteLine("熊猫吃竹子 " + Face);
}
}
public class MainFun
{
static void Main()
{
Animal[] anim = new Animal[3];
anim[0] = new dog();
anim[1] = new cat();
anim[2] = new panda();
anim[0].eat();
anim[1].eat();
anim[2].eat();
Console.ReadKey();
}
}
十六.接口
1.与类一样,在接口中可以定义一个和多个方法、属性、索引指示器和事件。
2.但与类不同的是,接口中仅仅是它们的声明,并不提供实现。因此接口是函数成员声明的集合。
3.如果类或结构从一个接口派生,则这个类或结构负责实现该接口中所声明的所有成员。一个接口可以从多个接口继承,而一个类或结构可以实现多个接口。
●接口成员声明不能包含任何修饰符,接口成员默认访问方式是public。
●接口成员只能是方法、属性、索引指示器和事件,不能是常量、域、操作符、构造函数或析构函数,不能包含任何静态成员
接口例子
public interface IExample
{
//所有接口成员都不能包括实现
string this[int index] {get;set;} //索引指示器声明
event EventHandler E; //事件声明
void F(int value); //方法声明
string P { get; set;} //属性声明
}
▉ 那么究竟抽象类和接口存在哪些区别呢?
●它们的派生类只能继承一个基类,即只能继承一个抽象类,但是可以继承多个接口。
●抽象类中可以定义成员的实现,但接口中不可以。
●抽象类中包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以。
●抽象类中的成员可以私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员,但接口中的成员必须是公共的
▉抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象系列的基类,共享某些主要特性,例如共同的目的和结构。
接口则主要用于类,这些类在基础水平上有所不同,但仍然可以完成某些相同的任务。
public abstract class oneClass
{
private string id = "";
private string name = "";
public string id_str
{
get { return id; }
set { id = value; }
}
public string name_str
{
get { return name; }
set { name = value;}
}
public abstract string ShowInfo(string namee,string idd);//抽象函数不能有函数体存在,becuase declare that is marked abstract
}
public class AbstractorClass : oneClass
{
public override string ShowInfo(string name_strs, string id_strs)
{
name_str = name_strs;
id_str = id_strs;
string dis_trs = name_str + "<-->" + id_str;
return dis_trs;
}
}
十七.delegate委托,delegate类的声明格式:
属性集 修饰符 delegate 函数返回类型 定义的代表识别符(函数的形参列表)
such as:
using system;
deletgate int mydelegate();
public class MyClass
{
public int InstanceMethod()//非静态的方法,注意方法为int类型,无参数
{
Console.WriteLine("调用了非静态的方法。");
return 0;
}
static public int StaticMethod()//静态方法,注意方法为int类型,无参数
{ Console.WriteLine("调用了静态的方法。");
return 0;
}
}
public class Test
{
static public void Main ()
{
MyClass p = new MyClass(); //用new建立代表类MyDelegate对象,d中存储非静态的方法InstanceMethod的地址
MyDelegate d=new MyDelegate(p.InstanceMethod);//参数是被代表的方法
d();//调用非静态方法//用new建立代表类MyDelegate对象,d中存储静态的方法StaticMethod的地址
d=new MyDelegate(MyClass.StaticMethod);//参数是被代表的方法
d();//调用静态方法
}
}
程序的输出结果是:
调用了非静态的方法。
调用了静态的方法。
十八.方法参数的种类(给一个引用类型赋值将复制到一个对象的引用,而给一个值类型赋值将复制一个对象的值)
引用参数是说ref吧,输出参数是说out吧;都是为了能让方法多返回一个值
一般情况下不加ref或者out的时候,传值类型的数据进去实际上传进去的是源数据的一个副本,也就是在内存中新开辟了一块空间,这里面存的值是与源数据相等的,
这也就是为什么在传值类型数据的时候你如果不用return是无法修改原值的原因。
但是你如果用了REF,或者OUT,这一切问题都解决了,因为他们传的是地址。
out比起REF来说,还有一个用法就是可以作为多返回值来用,都知道函数只能有一个返回值,C#里,如果你想让一个函数有多个返回值,那么OUT能很容易解决。
如 ref的参数要求使用前一定要初始化,在方法中改变后,这种改变会保留,相当于多了一个返回值
out参数要求使用前一定要初始化,但是在方法内部要对该参数重新初始化,再使用,也就是说,外部的初始化对内部没有影响。
out参数会保留在内部方法做出的改变值参数,就是传递的值的拷贝啊内部对该值做的任何改变不会保留到外部
▉ 值参数,不含任何修饰符。
值参数(value parameters)未用 ref 或 out 修饰符声明的参数为值参数
▉ 引用参数,以ref修饰符声明。
使用ref关键字声明的参数为引用参数
▉ 输出参数,以out修饰符声明。
使用out关键字声明的参数为输出参数
▉ 数组参数,以params修饰符声明。
//win form 例子
public class program
{
static void value_parameters(string str)
{
str=str + ":hello";
}
static void reference_parameters(ref string str)
{
str=str + ":hello";
}
static void output_parameters(out string str)
{
str = "hello";
}
static void main(string[] args)
{
string value_x="ff";
string reference_y="yy";
string output_z; //要做为输出参数所以没必要必须初始化。
value_parameters(value_x) //这种变化不会在函数体外保留,因为参数是在栈内存上开辟空间,函数调用完毕自动释放变量占用空间。
reference_parameters(ref reference_y); //这种变化会一直保留,因为引用参数是在堆内存上开辟空间,只有主程序关闭时通过回收器GC来释放对上的空间。
output_parameters(out output_z); //输出参数经过函数调用处理后,给函数体内部参数进行初始化值操作。
messagebox.show(x);
messagebox.show(y);
}
}
十九.事件
事件声明
public delegate void EventHandler(object sender,EventArgs e); //delegate的声明;EventHandler委托可以没有返回值,参数为(object sender,EventArgs e)的函数
public class Button:Control //定义一个按钮类Button组件
{
//按钮类Button其它成员定义
public event EventHandler Click;//声明一个事件Click,是委托类引用变量
protected void OnClick(EventArgs e)//Click事件发生,自动触发OnClick方法
{
if(Click!=null)//如果Click已代表了事件处理函数,执行这个函数
Click(this,e);
}
public void Reset()
{
{Click=null;}
}
在这个例子中,Click事件发生,应有代码保证(未列出)自动触发OnClick方法。Click是类Button的一个事件,同时也是代表EventHandler类的引用变量,
如令Click代表事件处理函数,该函数完成Click事件应完成的功能,Click事件发生时,执行事件处理函数。
usingSystem;
class Person//类的定义,class是保留字,表示定义一个类,Person是类名
{
private string name="张三"; //类的数据成员声明
private int age=12; //private表示私有数据成员
public void Display() //类的方法(函数)声明,显示姓名和年龄
{
Console.WriteLine("姓名:{0},年龄:{1}",name,age);
}
public void SetName(string PersonName)//修改姓名的方法(函数)
{
name=PersonName;
}
public void SetAge(int PersonAge)
{
age=PersonAge;
}
}
一. 类的构造函数
a。类在new出一个实例的时候会自动调用构造函数。
b。一些类在创建的时候需要初始化工作,这个工作可以由构造函数完成。
c。构造函数和类名称相同,没有返回值
public Person(string name,int age)
{
Name=name;
Age=age;
}
当用:
Person OnePerson=new Person(“张五”,20) 语句生 Person 类对象时,将自动调用以上构造函数.
请注意如何把参数传递给构造函数。
二.类的析构函数
a。变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销。类的对象被撤销时,将自动调用析构函数。
b。一些善后工作可放在析构函数中完成。
C。析构函数的名称 “~类名” ,无返回类型 也无参数。
~Person()
{
}
C#中类析构函数不能显示地被调用,它是被垃圾收集器撤销不被使用的对象时,进行自动调用的。
三.类的构造函数的重载
在C#语言中,同一个类中函数名称相同,参数类型不同或者参数个数不同的函数,这叫函数重载。
仅仅返回值不同不能看做不同的函数。
usingSystem;
namespace e1//定义以下代码所属命名空间,意义见以后章节
{
class Person
{
private String name="张三";//类的数据成员声明
private int age=12;
public void Display()//类的方法(函数)声明,显示姓名和年龄
{
Console.WriteLine("姓名:{0},年龄:{1}",name,age);
}
public void SetName(string PersonName)//指定修改姓名的方法(函数)
{
name=PersonName;
}
public void SetAge(int PersonAge)//指定修改年龄的方法(函数)
{
age=PersonAge;
}
public Person (string Name,int Age)//构造函数,函数名和类同名,无返回值
{
name=Name;
age=Age;
}
public Person()//类的构造函数重载
{
name="田七";
age=12;
}
}
class Class1
{
static void Main(string[] args)
{
Person OnePerson=new Person("李四",30);//生成类的对象
OnePerson.Display();
//下句错误,在其它类(Class1类)中,不能直接修改Person类中的私有成员。
//OnePerson.name="王五";
//只能通过Person类中公有方法SetName修改Person类中的私有成员name。
OnePerson.SetName("王五");
OnePerson.SetAge(40);
OnePerson.Display();
OnePerson=new Person();
OnePerson.Display();
}
}
}
键入CTRL+F5运行后,显示的效果是:
姓名: 李四,年龄:30
姓名: 王五,年龄:40
姓名: 田七,年龄:12
四. C#的数据类型
可分为三种类型:值类型,应用类型,指针类型(仅仅用于非安全代码中)
五.测试运算符is
using System;
class Test
{
public static void Main()
{
Console.WriteLine(1 is int);
Console.WriteLine(1 is float);
Console.WriteLine(1.0f is float);
Console.WriteLine(1.0d is double);
}
}
输出为:
True
False
True
True
六. 溢出检查操作符 checked 和 unchecked
如例:在进行整型算术运算(如+、-、*、/等)或从一种整型显式转换到另一种整型时,有可能出现运算结果超出这个结果所属类型值域的情况
类似这样的情况我们称之为溢出。
整型算术运算表达式可以用checked或unchecked溢出检查操作符,决定在编译和运行时是否对表达式溢出进行检查。
如果表达式不使用溢出检查操作符或使用了checked操作符,常量表达式溢出,在编译时将产生错误,表达式中包含变量,程序运行时执行该表达式产生溢出,将产生异常提示信息。
而使用了unchecked操作符的表达式语句,即使表达式产生溢出,编译和运行时都不会产生错误提示。但这往往会出现一些不可预期的结果,所以使用unchecked操作符要小心。
such as:
using system;
class Class1
{
static void Mian(string[] args)
{
const int x=int.MaxValue;
checked //check overflow
{
int y=x;
Console.WriteLine("y eque result is {0}",y);
}
unchecked //don't check overflow
{
int z=x*3;
Console.WriteLine("z value is {0}",z);
}
}
}
七.类的继承
派生类的声明格式如下:
属性 类修饰符 class 派生类名:基类名
{类体}
八.Base 关键字
base关键字用于从派生类中访问基类成员
1. 在定义派生类的构造函数中,指明要调用的基类构造函数,由于基类可能有多个构造函数,根据base后的参数类型和个数,指明要调用哪一个基类构造函数。
2. 在派生类的方法中调用基类中被派生类覆盖的方法
九.覆盖基类成员
在派生类中,通过声明与基类完全相同新成员,可以覆盖基类的同名成员,完全相同是指函数类型、函数名、参数类型和个数都相同
如下例中的方法Display()。派生类覆盖基类成员不算错误,但会导致编译器发出警告。如果增加new修饰符,表示认可覆盖,编译器不再发出警告。
请注意,覆盖基类的同名成员,并不是移走基类成员,只是必须用如下格式访问基类中被派生类覆盖的方法:base.Display()。
十.base和this用法(着重说明base关键字)
-------------------------------------------------------------------------------------------------------------------------------------
base关键字用于从派生类中访问基类成员,它有两种基本用法:
1. 在定义派生类的构造函数中,指明要调用的基类构造函数,由于基类可能有多个构造函数,根据base后的参数类型和个数,指明要调用哪一个基类构造函数。
2. 在派生类的方法中调用基类中被派生类覆盖的方法时.使用base关键字
-------------------------------------------------------------------------------------------------------------------------------------
base是子类中引用父类
1.base 是为实现太多而设计的
2.this 和 base关键字只指定一个构造函数,也就是说不能同时将base和this作用在一个构造函数上。
3.简单来说,base用于在派生类中访问重写的基类成员;
而this用于访问本类的成员,当然也可包括继承而来的共有的和保护成员
another:
除了base,访问基类成员的另一种方式是: 显式的类型转换来实现,只是该方法不能为静态的方法。
such as:
class Employee:Person //Person类是基类
{
private string department;//部门,新增数据成员
private decimal salary;//薪金,新增数据成员
public employee(string name,int age,string d,decimal s):base(name,age)
{
//注意base的第一种用法,根据参数调用指定基类构造函数,注意参数的传递
department=D;
salary=S;
}
public new void Display()//覆盖基类Display()方法,注意new,不可用override
{
base.Display();//访问基类被覆盖的方法,base的第二种用法
Console.WriteLine("部门:{0}薪金:{1}",department,salary);
}
}
修改主函数如下:
class Class1
{
staticvoidMain(string[]args)
{
Employee OneEmployee=new Employee("李四",30,"计算机系",2000);
OneEmployee.Display();
}
}
十一.C#语言类继承有如下特点
1. C#语言只允许单继承,即派生类只能有一个基类。
2. C#语言继承是可以传递的,如果C从B派生,B从A派生,那么C不但继承B的成员,还要继承A中的成员.
3. 派生类可以添加新成员,但不能删除基类中的成员。
4. 派生类不能继承基类的构造函数、析构函数和事件。但能继承基类的属性。
5. 派生类可以覆盖基类的同名成员,如果在派生类中覆盖了基类同名成员,基类该成员在派生类中就不能被直接访问,只能通过base.基类方法名访问。
6. 派生类对象也是其基类的对象,但基类对象却不是其派生类的对象。
十二.
在类中常量定义好后,在方法或者主函数调用时不能进行修改。
get和set方法对属性进行读写,是的类实例能直接对私有变量进行修改。
class csharp
{
private string mynames = "jinhua";
private int ages = 100;
public void setname(string trs)
{
mynames=trs;
}
public void setage(int aes)
{
ages=aes;
}
public string name
{
get { return mynames; }
set { mynames = value; }
}
public int age
{
get { return age; }
set { ages = value; }
}
public csharp()
{
Console.WriteLine("name is {0},age is {1}", mynames, ages);
}
public csharp(string myname, int age)
{
mynames = myname;
ages = age;
}
public void print()
{
Console.WriteLine("name is {0},age is {1}", mynames, ages);
}
static void Main(string[] args)
{
csharp cp = new csharp("qujinhua",1006);
cp.name = "yingxin";
cp.age = 1008;
cp.print();
csharp cp2 = new csharp();
cp2.setname("asd");
cp2.setage(123);
cp2.print();
Console.ReadLine();
}
}
十三.静态方法和实例方法
1.静态方法
a.使用static修饰符的方法是静态方法,不是用static修饰符的是实例方法。
b.不论类是否生成对象与否,静态方法都可以被使用,格式如下:
类名.静态方法名称
c.静态方法只能"调用或者使用"该静态方法所在类的“静态数据成员”和“静态方法"
2.函数重载
在同一个类中函数名称相同,函数参数类型或参数个数不同,认为是不相同的函数;仅返回值不同,不能看作不同的函数,这叫做函数的重载。
十四.类的多态性
C sharp语言中支持两种类型的多态性
1.第一种是编译时的多态性:一个类的对象调用多个同名函数,在编译时根据函数的参数类型或参数的个数确定调用哪一个同名函数,而实现何种操作。
(一句话,编译时,一个类对象调用多个同名函数,以实现其某种操作。通过函数重载实现。)
编译时的多态性是通过函数的重载实现的。
2.第二种是运行时的多态性:系统运行时,不同对象调用一个名字相同,参数类型和参数个数完全一样的函数,会完成不同的操作。
(一句话:系统运行时,多个对象调用一个完全相同的函数,完成不同的操作。通过虚函数实现。)
运行时的多态时通过C#的”虚方法实现“
在类方法声明前加 "Virtual"修饰符,即这就是虚方法
3. 备注:“覆盖基类函数成员”
3.1 派生类覆盖基类中的函数成员(完全相同,函数类型,参数个数,参数类型,函数名称)在基类中声明必须使用new关键字,不然编译不通过。
using System;
class A
{
public void Display()//非虚方法
{
Console.Write("非虚方法");
}
public virtual void Display_virtual()//虚方法
{
Console.Write("虚方法");
}
}
class B:A
{
new public void Display()
{
Console.Write("非虚方法的覆盖");
}
public ovreride void Display_virtual()
{
Console.Write("虚方法的覆盖");
}
}
a. “覆盖基类的同名非虚方法,注意使用new“
new public void Display()
{......}
b. 覆盖基类的同名虚方法(虚函数),注意使用override
public ovreride void Display_virtual()
{......}
4. 备注:“解释virtual方法的使用”
class A
{
public virtual void PrintFields()
{
Console .WriteLine ("我是A的方法");
}
}
class B:A
{
public override void PrintFields()
{
Console.WriteLine("我是B的方法");
}
}
class C:A
{
}
class Test
{
static void Main(string[] args)
{
A a;
a = new B();
a.PrintFields();//我是B的方法
a = new C();
a.PrintFields();//我是A的方法
}
}
十五.抽象类和抽象方法
abstract修饰符可以和类、方法、属性、索引器及事件一起使用,在类声明中使用abstract修饰符以表明这个类只能是其他类的基类。
A.抽象类的特性
(1)抽象类不能被实例化
(2)抽象类可以包含抽象方法和抽象访问器
(3)不能用sealed修饰符修改抽象类,因为抽象类本身就是用来给其他类继承的
(4)抽象类的非抽象子类必须实现其继承的所有抽象方法和抽象访问器
B.抽象方法
(1)抽象方法是隐式的虚方法
(2)抽象方法只允许声明在抽象类中
(3)抽象方法不能提供实际的实现,所以没有方法体;抽象方法的实现是在非抽象的派生类中以override重写实现的
(4)抽象方法声明中不可以使用static或者virtual修饰符
(5)abstract关键字不能修饰静态方法或静态属性
C.抽象类的构造函数
(1)不要再抽象类中定义public或protected internal访问权限的构造函数
(2)应在抽象类中定义protected或private访问权限的构造函数
(3)如果在抽象类中定义一个protected构造函数,则在实例化派生类时,基类可以执行初始化任务
D.抽象方法和虚方法的区别
虚方法有实现部分,并且派生类对其重写是可选的;抽象方法没有实现部分,并且强制非抽象派生类对其重写
using System;
using System.Collections;
/抽象类
public abstract class Animal
{
protected string face;
//声明为protected的构造函数,在实例化派生类时,基类可以执行初始化工作
protected Animal() { this.face = "^_^"; /*抽象类构造函数初始化的例子*/}
public abstract void eat();
public abstract string Face
{
get;
}
}
public class dog : Animal
{
//重写基类中的抽象访问器Face
public override string Face
{
get
{
return face;
}
}
//重写基类中的抽象方法eat
public override void eat()
{
Console.WriteLine("狗吃骨头 " + Face);
}
}
public class cat : Animal
{
public override string Face
{
get
{
return face;
}
}
public override void eat()
{
Console.WriteLine("猫吃鱼 " + Face);
}
}
public class panda : Animal
{
public override string Face
{
get
{
return face;
}
}
public override void eat()
{
Console.WriteLine("熊猫吃竹子 " + Face);
}
}
public class MainFun
{
static void Main()
{
Animal[] anim = new Animal[3];
anim[0] = new dog();
anim[1] = new cat();
anim[2] = new panda();
anim[0].eat();
anim[1].eat();
anim[2].eat();
Console.ReadKey();
}
}
十六.接口
1.与类一样,在接口中可以定义一个和多个方法、属性、索引指示器和事件。
2.但与类不同的是,接口中仅仅是它们的声明,并不提供实现。因此接口是函数成员声明的集合。
3.如果类或结构从一个接口派生,则这个类或结构负责实现该接口中所声明的所有成员。一个接口可以从多个接口继承,而一个类或结构可以实现多个接口。
●接口成员声明不能包含任何修饰符,接口成员默认访问方式是public。
●接口成员只能是方法、属性、索引指示器和事件,不能是常量、域、操作符、构造函数或析构函数,不能包含任何静态成员
接口例子
public interface IExample
{
//所有接口成员都不能包括实现
string this[int index] {get;set;} //索引指示器声明
event EventHandler E; //事件声明
void F(int value); //方法声明
string P { get; set;} //属性声明
}
▉ 那么究竟抽象类和接口存在哪些区别呢?
●它们的派生类只能继承一个基类,即只能继承一个抽象类,但是可以继承多个接口。
●抽象类中可以定义成员的实现,但接口中不可以。
●抽象类中包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以。
●抽象类中的成员可以私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员,但接口中的成员必须是公共的
▉抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象系列的基类,共享某些主要特性,例如共同的目的和结构。
接口则主要用于类,这些类在基础水平上有所不同,但仍然可以完成某些相同的任务。
public abstract class oneClass
{
private string id = "";
private string name = "";
public string id_str
{
get { return id; }
set { id = value; }
}
public string name_str
{
get { return name; }
set { name = value;}
}
public abstract string ShowInfo(string namee,string idd);//抽象函数不能有函数体存在,becuase declare that is marked abstract
}
public class AbstractorClass : oneClass
{
public override string ShowInfo(string name_strs, string id_strs)
{
name_str = name_strs;
id_str = id_strs;
string dis_trs = name_str + "<-->" + id_str;
return dis_trs;
}
}
十七.delegate委托,delegate类的声明格式:
属性集 修饰符 delegate 函数返回类型 定义的代表识别符(函数的形参列表)
such as:
using system;
deletgate int mydelegate();
public class MyClass
{
public int InstanceMethod()//非静态的方法,注意方法为int类型,无参数
{
Console.WriteLine("调用了非静态的方法。");
return 0;
}
static public int StaticMethod()//静态方法,注意方法为int类型,无参数
{ Console.WriteLine("调用了静态的方法。");
return 0;
}
}
public class Test
{
static public void Main ()
{
MyClass p = new MyClass(); //用new建立代表类MyDelegate对象,d中存储非静态的方法InstanceMethod的地址
MyDelegate d=new MyDelegate(p.InstanceMethod);//参数是被代表的方法
d();//调用非静态方法//用new建立代表类MyDelegate对象,d中存储静态的方法StaticMethod的地址
d=new MyDelegate(MyClass.StaticMethod);//参数是被代表的方法
d();//调用静态方法
}
}
程序的输出结果是:
调用了非静态的方法。
调用了静态的方法。
十八.方法参数的种类(给一个引用类型赋值将复制到一个对象的引用,而给一个值类型赋值将复制一个对象的值)
引用参数是说ref吧,输出参数是说out吧;都是为了能让方法多返回一个值
一般情况下不加ref或者out的时候,传值类型的数据进去实际上传进去的是源数据的一个副本,也就是在内存中新开辟了一块空间,这里面存的值是与源数据相等的,
这也就是为什么在传值类型数据的时候你如果不用return是无法修改原值的原因。
但是你如果用了REF,或者OUT,这一切问题都解决了,因为他们传的是地址。
out比起REF来说,还有一个用法就是可以作为多返回值来用,都知道函数只能有一个返回值,C#里,如果你想让一个函数有多个返回值,那么OUT能很容易解决。
如 ref的参数要求使用前一定要初始化,在方法中改变后,这种改变会保留,相当于多了一个返回值
out参数要求使用前一定要初始化,但是在方法内部要对该参数重新初始化,再使用,也就是说,外部的初始化对内部没有影响。
out参数会保留在内部方法做出的改变值参数,就是传递的值的拷贝啊内部对该值做的任何改变不会保留到外部
▉ 值参数,不含任何修饰符。
值参数(value parameters)未用 ref 或 out 修饰符声明的参数为值参数
▉ 引用参数,以ref修饰符声明。
使用ref关键字声明的参数为引用参数
▉ 输出参数,以out修饰符声明。
使用out关键字声明的参数为输出参数
▉ 数组参数,以params修饰符声明。
//win form 例子
public class program
{
static void value_parameters(string str)
{
str=str + ":hello";
}
static void reference_parameters(ref string str)
{
str=str + ":hello";
}
static void output_parameters(out string str)
{
str = "hello";
}
static void main(string[] args)
{
string value_x="ff";
string reference_y="yy";
string output_z; //要做为输出参数所以没必要必须初始化。
value_parameters(value_x) //这种变化不会在函数体外保留,因为参数是在栈内存上开辟空间,函数调用完毕自动释放变量占用空间。
reference_parameters(ref reference_y); //这种变化会一直保留,因为引用参数是在堆内存上开辟空间,只有主程序关闭时通过回收器GC来释放对上的空间。
output_parameters(out output_z); //输出参数经过函数调用处理后,给函数体内部参数进行初始化值操作。
messagebox.show(x);
messagebox.show(y);
}
}
十九.事件
事件声明
public delegate void EventHandler(object sender,EventArgs e); //delegate的声明;EventHandler委托可以没有返回值,参数为(object sender,EventArgs e)的函数
public class Button:Control //定义一个按钮类Button组件
{
//按钮类Button其它成员定义
public event EventHandler Click;//声明一个事件Click,是委托类引用变量
protected void OnClick(EventArgs e)//Click事件发生,自动触发OnClick方法
{
if(Click!=null)//如果Click已代表了事件处理函数,执行这个函数
Click(this,e);
}
public void Reset()
{
{Click=null;}
}
在这个例子中,Click事件发生,应有代码保证(未列出)自动触发OnClick方法。Click是类Button的一个事件,同时也是代表EventHandler类的引用变量,
如令Click代表事件处理函数,该函数完成Click事件应完成的功能,Click事件发生时,执行事件处理函数。