11.可以使用抽象函數重寫基類中的虛函數嗎?答:可以,但需使用 new 修飾符顯式聲明,表示隱藏了基類中該函數的實現
示例:
class BaseClass
{
public virtual void F()
{
Console.WriteLine("BaseClass.F");
}
}
abstract class DeriveClass : BaseClass
{
public new abstract void F();
}
12.密封類可以有虛函數嗎?答:可以,基類中的虛函數將隱式的轉化為非虛函數,但密封類本身不能再增加新的虛函數
示例:
class BaseClass
{
public virtual void F()
{
Console.WriteLine("BaseClass.F");
}
}
sealed class DeriveClass : BaseClass
{
//基類中的虛函數F被隱式的轉化為非虛函數
//密封類中不能再聲明新的虛函數G
//public virtual void G()
//{
// Console.WriteLine("DeriveClass.G");
//}
}
13.如果基類中的虛屬性只有一個屬性訪問器,那么繼承類重寫該屬性后可以有幾個屬性訪問器?如果基類中有 get 和 set 兩個呢?
答:如果基類中的虛屬性只有一個屬性訪問器,那么繼承類重寫該屬性后也應只有一個。如果基類中有 get 和 set 兩個屬性訪問器,那么繼承類中可以只有一個也可以同時有兩個屬性訪問器
14.abstract 可以和 virtual 一起使用嗎?可以和 override 一起使用嗎?
答:abstract 修飾符不可以和 static、virtual 和 override 修飾符一起使用
15.接口可以包含哪些成員?答:接口可以包含屬性、方法、索引指示器和事件,但不能包含常量、域、操作符、構造函數和析構函數,而且也不能包含任何靜態成員
16.類和結構的區別?答:
類:類是引用類型在堆上分配,類的實例進行賦值只是復制了引用,都指向同一段實際對象分配的內存
類有構造和析構函數
類可以繼承和被繼承
結構:
結構是值類型在棧上分配(雖然棧的訪問速度比較堆要快,但棧的資源有限放),結構的賦值將分配產生一個新的對象。
結構沒有構造函數,但可以添加。結構沒有析構函數
結構不可以繼承自另一個結構或被繼承,但和類一樣可以繼承自接口
示例:
根據以上比較,我們可以得出一些輕量級的對象最好使用結構,但數據量大或有復雜處理邏輯對象最好使用類。
如:Geoemtry(GIS 里的一個概論,在 OGC 標準里有定義) 最好使用類,而 Geometry 中點的成員最好使用結構
using System;
using System.Collections.Generic;
using System.Text;
namespace Example16
{
interface IPoint
{
double X
{
get;
set;
}
double Y
{
get;
set;
}
double Z
{
get;
set;
}
}
//結構也可以從接口繼承
struct Point: IPoint
{
private double x, y, z;
//結構也可以增加構造函數
public Point(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
public double X
{
get { return x; }
set { x = value; }
}
public double Y
{
get { return x; }
set { x = value; }
}
public double Z
{
get { return x; }
set { x = value; }
}
}
//在此簡化了點狀Geometry的設計,實際產品中還包含Project(坐標變換)等復雜操作
class PointGeometry
{
private Point value;
public PointGeometry(double X, double Y, double Z)
{
value = new Point(X, Y, Z);
}
public PointGeometry(Point value)
{
//結構的賦值將分配新的內存
this.value = value;
}
public double X
{
get { return value.X; }
set { this.value.X = value; }
}
public double Y
{
get { return value.Y; }
set { this.value.Y = value; }
}
public double Z
{
get { return value.Z; }
set { this.value.Z = value; }
}
public static PointGeometry operator +(PointGeometry Left, PointGeometry Rigth)
{
return new PointGeometry(Left.X + Rigth.X, Left.Y + Rigth.Y, Left.Z + Rigth.Z);
}
public override string ToString()
{
return string.Format("X: {0}, Y: {1}, Z: {2}", value.X, value.Y, value.Z);
}
}
class Program
{
static void Main(string[] args)
{
Point tmpPoint = new Point(1, 2, 3);
PointGeometry tmpPG1 = new PointGeometry(tmpPoint);
PointGeometry tmpPG2 = new PointGeometry(tmpPoint);
tmpPG2.X = 4;
tmpPG2.Y = 5;
tmpPG2.Z = 6;
//由于結構是值類型,tmpPG1 和 tmpPG2 的坐標并不一樣
Console.WriteLine(tmpPG1);
Console.WriteLine(tmpPG2);
//由于類是引用類型,對tmpPG1坐標修改后影響到了tmpPG3
PointGeometry tmpPG3 = tmpPG1;
tmpPG1.X = 7;
tmpPG1.Y = 8;
tmpPG1.Z = 9;
Console.WriteLine(tmpPG1);
Console.WriteLine(tmpPG3);
Console.ReadLine();
}
}
}
結果:
X: 1, Y: 2, Z: 3
X: 4, Y: 5, Z: 6
X: 7, Y: 8, Z: 9
X: 7, Y: 8, Z: 9
17.接口的多繼承會帶來哪些問題?
答:C# 中的接口與類不同,可以使用多繼承,即一個子接口可以有多個父接口。但如果兩個父成員具有同名的成員,就產生了二義性(這也正是 C# 中類取消了多繼承的原因之一),這時在實現時最好使用顯式的聲明
示例:
using System;
using System.Collections.Generic;
using System.Text;
namespace Example17
{
class Program
{
//一個完整的接口聲明示例
interface IExample
{
//屬性
string P
{
get;
set;
}
//方法
string F(int Value);
//事件
event EventHandler E;
//索引指示器
string this[int Index]
{
get;
set;
}
}
interface IA
{
int Count { get; set;}
}
interface IB
{
int Count();
}
//IC接口從IA和IB多重繼承
interface IC : IA, IB
{
}
class C : IC
{
private int count = 100;
//顯式聲明實現IA接口中的Count屬性
int IA.Count
{
get { return 100; }
set { count = value; }
}
//顯式聲明實現IB接口中的Count方法
int IB.Count()
{
return count * count;
}
}
static void Main(string[] args)
{
C tmpObj = new C();
//調用時也要顯式轉換
Console.WriteLine("Count property: {0}", ((IA)tmpObj).Count);
Console.WriteLine("Count function: {0}", ((IB)tmpObj).Count());
Console.ReadLine();
}
}
}
結果:
Count property: 100
Count function: 10000
18.抽象類和接口的區別?
答:
抽象類(abstract class)可以包含功能定義和實現,接口(interface)只能包含功能定義
抽象類是從一系列相關對象中抽象出來的概念, 因此反映的是事物的內部共性;接口是為了滿足外部調用而定義的一個功能約定, 因此反映的是事物的外部特性
分析對象,提煉內部共性形成抽象類,用以表示對象本質,即“是什么”
為外部提供調用或功能需要擴充時優先使用接口
19.別名指示符是什么?
答:
通過別名指示符我們可以為某個類型起一個別名
主要用于解決兩個命名空間內有同名類型的沖突或避免使用冗余的命名空間
別名指示符只在一個單元文件內起作用
示例:
Class1.cs:
using System;
using System.Collections.Generic;
using System.Text;
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01
{
class Class1
{
public override string ToString()
{
return "com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's Class1";
}
}
}
Class2.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02
{
class Class1
{
public override string ToString()
{
return "com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1";
}
}
}
主單元(Program.cs):
using System;
using System.Collections.Generic;
using System.Text;
//使用別名指示符解決同名類型的沖突
using Lib01Class1 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01.Class1;
using Lib02Class2 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02.Class1;
namespace Example19
{
class Program
{
static void Main(string[] args)
{
Lib01Class1 tmpObj1 = new Lib01Class1();
Lib02Class2 tmpObj2 = new Lib02Class2();
Console.WriteLine(tmpObj1);
Console.WriteLine(tmpObj2);
Console.ReadLine();
}
}
}
結果:
com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's Class1
com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1