C#学习笔记(八)——定义类的成员
一、成员的定义
1、定义字段
class Myclass { public int MyInt; }
可以使用readonly关键字,表示这个字段只能在执行构造函数的过程中赋值,或者由初始化语句赋值。
静态成员通过定义它的类来进行访问(MyClass.MyInt)
2、定义方法
class Myclass { public int MyInt; public string GetString() { return "Here is a string!"; } }
与override一样,也可以使用sealed指定在派生类中不能对这个方法作进一步的修改,。
使用extern可以在项目外部提供方法的实现代码。
3、定义属性
get和set区别:get是只读,set是只写。然后get块一定要有一个返回值,下面是示例。
private int myInt;
public int MyIntProp { get { return myInt; } set { } }
这样的话,由于myInt这个字段是私有的,外部成员时不能访问的,但是通过这个get和set就可以在外部修改了,但是前提是属性是共有的。
set是一个赋值的功能,但是set可以通过一系列操作来达到不同途径来设置方法。而且还可以这里加上出错的警告之类的。
然后就是get和set一样也可以在前面加上一系列的限定关键字。例如
protected set { myInt = value; }
4、一个demo
创建一个MyClass.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Exercise { class MyClass { public readonly string Name; private int intVal; public int Val { get { return intVal; } set { if(value>=0&&value<=10) { intVal = value; } else { throw (new ArgumentOutOfRangeException("Val", value, "Val must be assigned a value between 0 and 10.")); } } } public override string ToString() { return "Name:" + Name + "\nVal:" + Val; } public MyClass(string newName) { Name = newName; intVal=0; } } }
在Main.cs中添加
#region Using directives using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; #endregion namespace Exercise { class Program { static void Main(string[] args) { Console.WriteLine("Creating object myObj..."); MyClass myobj = new MyClass("My Object"); Console.WriteLine("myObj created."); for(int i=-1;i<=0;i++) { try { Console.WriteLine("\nAttemp to assign {0} to myObj.val...", i); myobj.Val = i; Console.WriteLine("Value {0} assigned to myObj.Val.", myobj.Val); } catch(Exception e) { Console.WriteLine("Exception {0} thrown.", e.GetType().FullName); Console.WriteLine("Message:\n\"{0}\"", e.Message); } } Console.WriteLine("\nOutputting myobj.Tostring()..."); Console.WriteLine(myobj.ToString()); Console.WriteLine("myobj.ToString() Output."); Console.ReadKey(); } } }
二、类的高级议题
1、隐藏基类方法
(1)当从基类中继承一个非抽象的成员的时候,也就继承了其实现代码。
如果继承的成员时虚拟的,就可以用override重写这段代码。
无论继承的成员是不是虚拟的,都可以隐藏基类的代码。
public class MyBaseClass { public void DoSometing() { //Base implementation } } public class MyDeriveClass:MyBaseClass { public void DoSometing() { //Derived class implementation, hides base implementation } }
尽管这段代码正常运行,但是还是会有一个waring,可以提醒我们是否有意隐藏这个成员。如果确实要隐藏这个成员,我们可以使用关键字new显式地表明意图。
public class MyDeriveClass:MyBaseClass { new public void DoSometing() { //Derived class implementation, hides base implementation } }
(2)如果重写基类中的方法,派生类中的基类方法会被取代,即使是通过基类类型进行的,情况也是相同的。
public class MyBaseClass { public virtual void DoSometing() { Console.WriteLine("FUCK"); //Base implementation } } public class MyDeriveClass:MyBaseClass { public override void DoSometing() { Console.WriteLine("FUCK you!"); //Derived class implementation, hides base implementation } }
MyDeriveClass myObj = new MyDerivedClass(); MyBaseClass myBaseObj; myBaseObj = myObj; myBaseObj.DoSomething();
明显,运算结果是FUCK you!
(3)还有就是可以使用隐藏基类的方法实现复写所能实现的功能。
2、调用重写或隐藏的基类方法
(1)重要性:
a、要对派生类的用户隐藏继承的公共成员,但仍能在类中访问其功能。
b、要给继承的虚拟成员添加实现代码,而不是地重写的新执行代码替换它。
(2)base关键字
使用base关键字可以使用基类中的隐藏的相应的方法。
base.DoSomething();
(3)this关键字
a、可以在类内部使用,引用对象的实例。
b、把当前对象实例的引用传递给一个方法。
c、限定本地类型的成员。
return this.someData
3、嵌套的类型定义
class MyClass { public class myNestClass { public int nestedFlassField; } }
如果内部定义的类是共有的,那么就可以在外部使用,但是要加上限定名称,例如
MyClass.myNestedClass myobj = new MyClass.myNestedClass();
如果嵌套的类声明为私有,或者声明为其他与执行该实例化的代码不兼容的访问级别就不能在外部访问。
三、接口的实现
1、定义方法
interface IMyInterface { }
接口成员的定义与类相似,但是有一些重要的区别:
但是如果要隐藏继承了基接口的成员,可以用关键字new来定义他们。
在接口中定义的属性可以定义访问块get和set中的哪一个能够用于该属性~~~~~~~~~~~~
interface IMyInterface { int MyInt(get;set;) }
2、在类中实现接口
实现接口的的类必须包含该接口所有成员的实现代码,且必须匹配指定的签名(包括匹配指定的get和set快),并且必须是公共的。
interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class Myclass:IMyInterface { public void DoSomething() { } public void DoSomethingElse() { } }
可以使用virtual 或者abstract来实现接口成员。还可以在基类上实现接口。
interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class Myclass:IMyInterface { public void DoSomething() { } } public class MyDerivedClass:Myclass,IMyInterface { public void DoSomethingElse() { } }
继承一个实现给定的基类,就意味着派生类隐式地支持这个接口。
当然如果基类中的方法定义为虚拟,那么派生类就可以替代这个方法,而不是隐藏它们。
(1)显示实现接口成员。
如果显示地实现接口成员,那么这个成员就是能通过接口来访问,不能通过该类来访问。
通过类访问:
Myclass myobj = new Myclass(); myobj.DoSomething();
通过接口访问:
Myclass myobj = new Myclass(); IMyinterface myInt = myobj; myInt.DoSomething();
(2)用非公共的可访问性添加属性存取器
简单的来说就是接口中如果定义了set而没有定义get,那么get就可以在类中添加,并且它的限制级更高。
四、部分类定义
就是使用部分类定义,把类的定义放在多个文件中。我们只需要在每个包含部分类定义的文件中对类使用partial关键字就好。
public partical class MyClass { }
应用于部分类的接口也会应用于整个类..
部分类只能继承同一个基类,因为C#规定类只能继承一个基类.
五、部分方法定义
在一个部分声明,另一个部分实现(同C++)
public partical class MyClass { partical void MyPartialMethod(); } public partical class MyClass { partical void MypartialMethod() { } }
六、一个demo
//这是卡片类
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ch10CardLib { public class Card { public Rank rank; public Suit suit; private Card() { throw new System.NotImplementedException(); } public Card(Suit newSuit, Rank newRank) { suit = newSuit; rank = newRank; } public string Tostring() { return "The" + rank + "of" + suit + "s"; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; //这是一副牌 namespace Ch10CardLib { public class Deck { private Card[] cards; public Deck() { cards = new Card[52]; for (int suitVal = 0; suitVal < 4; suitVal++) { for (int rankVal = 1; rankVal < 14; rankVal++) { cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal, (Rank)rankVal); } } } public Card Getcard(int cardNum) { if(cardNum>=0&&cardNum<=51) return cards[cardNum]; else throw(new System.ArgumentOutOfRangeException("cardNum",cardNum,"Value must between 0 and 51")); } public void Shuffle() { Card[] newDeck = new Card[52]; bool[] assigned = new bool[52]; Random sourceGen = new Random(); for(int i=0;i<52;i++) { int destCard=0; bool foundCard=false; while(foundCard==false) { destCard = sourceGen.Next(52); if(assigned[destCard]==false) { foundCard = true; } } assigned[destCard]=true; newDeck[destCard]=cards[i]; } newDeck.CopyTo(cards,0); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; //枚举
namespace Ch10CardLib { public enum Rank { Ace = 1, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ch10CardLib { public enum Suit { Club, Dimaond, Heart, Spade, } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Ch10CardLib; //主程序 namespace Ch10CardClient { class Program { static void Main(string[] args) { Deck myDeck = new Deck(); myDeck.Shuffle(); for(int i=0;i<52;i++) { Card tempCard = myDeck.Getcard(i); Console.WriteLine(tempCard.Tostring()); if (i != 51) Console.Write(", "); else Console.WriteLine(); } Console.ReadKey(); } } }
最后的最后,记住一点很好用的地方:
那就是用在类图处,可以直接使用工具栏提供好的类、枚举之类的工具快速创建,十分方便。