11.可使用抽象函數重寫基類中的虛函數嗎?
答:
能夠,但需使用 new 修飾符顯式聲明,表示隱藏了基類中該函數的實現
示例:
Code
class BaseClass
{
public
virtual
void F()
{
Console.WriteLine("BaseClass.F");
}
}
abstract
class DeriveClass : BaseClass
{
public
new
abstract
void F();
}
12.密封類能夠有虛函數嗎?
答:
能夠,基類中的虛函數將隱式的轉化爲非虛函數,但密封類自己不能再增長新的虛函數
示例:
Code
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 中點的成員最好使用結構
Code
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# 中類取消了多繼承的緣由之一),這時在實現時最好使用顯式的聲明
示例:
Code
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.別名指示符是什麼?
答:
經過別名指示符咱們能夠爲某個類型起一個別名
主要用於解決兩個命名空間內有同名類型的衝突或避免使用冗餘的命名空間
別名指示符只在一個單元文件內起做用
示例:
Code
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;
//使用別名指示符解決同名類型(51aspx)的衝突
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
20.如何釋放非託管資源?
答:
.NET 平臺在內存管理方面提供了GC(Garbage Collection),負責自動釋放託管資源和內存回收的工做,但它沒法對非託管資源進行釋放,這時咱們必須本身提供方法來釋放對象內分配的非託管資源,好比你在對象的實現代碼中使用了一個COM對象
最簡單的辦法,能夠經過實現protected void Finalize()(析構函數會在編譯時變成這個東東)來釋放非託管資源,由於GC在釋放對象時會檢查該對象是否實現了 Finalize() 方法,若是是則調用它。但,聽說這樣會下降效率。。。
有一種更好的,那就是經過實現一個接口顯式的提供給客戶調用端手工釋放對象的方法,而不是傻傻的等着GC來釋放咱們的對象(況且效率又那麼低)
System 命名空間內有一個 IDisposable 接口,拿來作這事很是合適,就免得咱們本身再聲明一個接口了
另外補充一句,這種實現並不必定要使用了非託管資源後才用,若是你設計的類會在運行時有大些的實例(象 GIS 中的Geometry),爲了優化程序性能,你也能夠經過實現該接口讓客戶調用端在確認不須要這些對象時手工釋放它們
示例:
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace Example20
{
class Program
{
class Class1 : IDisposable
{
//析構函數,編譯後變成 protected void Finalize(),GC會在回收對象前會調用調用該方法
~Class1()
{
Dispose(false);
}
//經過實現該接口,客戶能夠顯式地釋放對象,而不須要等待GC來釋放資源,聽說那樣會下降效率
void IDisposable.Dispose()
{
Dispose(true);
}
//將釋放非託管資源設計成一個虛函數,提供在繼承類中釋放基類的資源的能力
protected
virtual
void ReleaseUnmanageResources()
{
//Do something
}
//私有函數用以釋放非託管資源
private
void Dispose(bool disposing)
{
ReleaseUnmanageResources();
//爲true時表示是客戶顯式調用了釋放函數,需通知GC不要再調用對象的Finalize方法
//爲false時確定是GC調用了對象的Finalize方法,因此沒有必要再告訴GC你不要調用個人Finalize方法啦
if (disposing)
{
GC.SuppressFinalize(this);
}
}
}
static
void Main(string[] args)
{
//tmpObj1沒有手工釋放資源,就等着GC來慢慢的釋放它吧
Class1 tmpObj1 =
new Class1();
//tmpObj2調用了Dispose方法,傳說比等着GC來釋放它效率要調一些
//我的認爲是由於要逐個對象的查看其元數據,以確認是否實現了Dispose方法吧
//固然最重要的是咱們能夠本身肯定釋放的時間以節省內存,優化程序運行效率
Class1 tmpObj2 =
new Class1();
((IDisposable)tmpObj2).Dispose();
}
}
}
21.P/Invoke是什麼?
答:
在受控代碼與非受控代碼進行交互時會產生一個事務(transition) ,這一般發生在使用平臺調用服務(Platform Invocation Services),即P/Invoke
如調用系統的 API 或與 COM 對象打交道,經過 System.Runtime.InteropServices 命名空間
雖然使用 Interop 很是方便,但據估計每次調用事務都要執行 10 到 40 條指令,算起來開銷也很多,因此咱們要儘可能少調用事務
若是非用不可,建議本着一次調用執行多個動做,而不是屢次調用每次只執行少許動做的原則
22.StringBuilder 和 String 的區別?
答:
String 雖然是一個引用類型,但在賦值操做時會產生一個新的對象,而 StringBuilder 則不會
因此在大量字符串拼接或頻繁對某一字符串進行操做時最好使用 StringBuilder,不要使用 String
示例:
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace Example22
{
class Program
{
static
void Main(string[] args)
{
const
int cycle =
100000;
long vTickCount = Environment.TickCount;
String str =
null;
for (int i =
0; i < cycle; i++)
str += i.ToString();
Console.WriteLine(&quot;String: {0} MSEL&quot;, Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
//看到這個變量名我就生氣,奇怪爲何你們都使它呢? :)
StringBuilder sb =
new StringBuilder();
for (int i =
0; i < cycle; i++)
sb.Append(i);
Console.WriteLine(&quot;StringBuilder: {0} MSEL&quot;, Environment.TickCount - vTickCount);
Console.ReadLine();
}
}
}
結果:
String: 102047 MSEL
StringBuilder: 46 MSEL
23.explicit 和 implicit 的含義?
答:
explicit 和 implicit 屬於轉換運算符,如用這二者可讓咱們自定義的類型支持相互交換
explicti 表示顯式轉換,如從 A -> B 必須進行強制類型轉換(B = (B)A)
implicit 表示隱式轉換,如從 B -> A 只需直接賦值(A = B)
隱式轉換可讓咱們的代碼看上去更漂亮、更簡潔易懂,因此最好多使用 implicit 運算符。不過!若是對象自己在轉換時會損失一些信息(如精度),那麼咱們只能使用 explicit 運算符,以便在編譯期就能警告客戶調用端
示例:
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace Example23
{
class Program
{
//本例靈感來源於大話西遊經典臺詞「神仙?妖怪?」--主要是我實在想不出什麼好例子了
class Immortal
{
public
string name;
public Immortal(string Name)
{
name = Name;
}
public
static
implicit
operator Monster(Immortal value)
{
return
new Monster(value.name +
&quot;:神仙變妖怪?偷偷下凡便可。。。&quot;);
}
}
class Monster
{
public
string name;
public Monster(string Name)
{
name = Name;
}
public
static
explicit
operator Immortal(Monster value)
{
return
new Immortal(value.name +
&quot;:妖怪想當神仙?再去修煉五百年!&quot;);
}
}
static
void Main(string[] args)
{
Immortal tmpImmortal =
new Immortal(&quot;紫霞仙子&quot;);
//隱式轉換
Monster tmpObj1 = tmpImmortal;
Console.WriteLine(tmpObj1.name);
Monster tmpMonster =
new Monster(&quot;孫悟空&quot;);
//顯式轉換
Immortal tmpObj2 = (Immortal)tmpMonster;
Console.WriteLine(tmpObj2.name);
Console.ReadLine();
}
}
}
結果:
紫霞仙子:神仙變妖怪?偷偷下凡便可。。。
孫悟空:妖怪想當神仙?再去修煉五百年!
24.params 有什麼用?
答:
params 關鍵字在方法成員的參數列表中使用,爲該方法提供了參數個數可變的能力
它在只能出現一次而且不能在其後再有參數定義,以前能夠
示例:
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class App
{
//第一個參數必須是整型,但後面的參數個數是可變的。
//並且因爲定的是object數組,全部的數據類型均可以作爲參數傳入
public
static
void UseParams(int id, params
object[] list)
{
Console.WriteLine(id);
for (int i =
0; i < list.Length; i++)
{
Console.WriteLine(list);
}
}
static
void Main()
{
//可變參數部分傳入了三個參數,都是字符串類型
UseParams(1, &quot;a&quot;, &quot;b&quot;, &quot;c&quot;);
//可變參數部分傳入了四個參數,分別爲字符串、整數、浮點數和雙精度浮點數數組
UseParams(2, &quot;d&quot;, 100, 33.33, new
double[] { 1.1, 2.2 });
Console.ReadLine();
}
}
}
結果:
1
a
b
c
2
d
100
33.33
System.Double[]
25.什麼是反射?
答:
反射,Reflection,經過它咱們能夠在運行時得到各類信息,如程序集、模塊、類型、字段、屬性、方法和事件
經過對類型動態實例化後,還能夠對其執行操做
通常用於插件式框架程序和設計模式的實現,固然反射是一種手段能夠充分發揮其能量來完成你想作的任何事情(前面好象見過一位高人用反射調用一個官方類庫中未說明的函數。。。)
示例:
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace Example25Lib
{
public
class Class1
{
private
string name;
private
int age;
//若是顯式的聲明瞭無參數構造函數,客戶端只須要用程序集的CreateInstance便可實例化該類
//在此特地不實現,以便在客戶調用端體現構造函數的反射實現
//public Class1()
//{
//}
public Class1(string Name, int Age)
{
name = Name;
age = Age;
}
public
void ChangeName(string NewName)
{
name = NewName;
}
public
void ChangeAge(int NewAge)
{
age = NewAge;
}
public
override
string ToString()
{
return
string.Format(&quot;Name: {0}, Age: {1}&quot;, name, age);
}
}
}
反射實例化對象並調用其方法,屬性和事件的反射調用略去
Code
using System;
using System.Collections.Generic;
using System.Text;
//注意添加該反射的命名空間
using System.Reflection;
namespace Example25
{
class Program
{
static
void Main(string[] args)
{
//加載程序集
Assembly tmpAss = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory +
&quot;Example25Lib.dll&quot;);
//遍歷程序集內全部的類型,並實例化
Type[] tmpTypes = tmpAss.GetTypes();
foreach (Type tmpType in tmpTypes)
{
//獲取第一個類型的構造函數信息
ConstructorInfo[] tmpConsInfos = tmpType.GetConstructors();
foreach (ConstructorInfo tmpConsInfo in tmpConsInfos)
{
//爲構造函數生成調用的參數集合
ParameterInfo[] tmpParamInfos = tmpConsInfo.GetParameters();
object[] tmpParams =
new
object[tmpParamInfos.Length];
for (int i =
0; i < tmpParamInfos.Length; i++)
{
tmpParams = tmpAss.CreateInstance(tmpParamInfos.ParameterType.FullName);
if (tmpParamInfos.ParameterType.FullName ==
&quot;System.String&quot;)
{
tmpParams =
&quot;Clark&quot;;
}
}
//實例化對象
object tmpObj = tmpConsInfo.Invoke(tmpParams);
Console.WriteLine(tmpObj);
//獲取全部方法並執行
foreach (MethodInfo tmpMethod in tmpType.GetMethods())
{
//爲方法的調用建立參數集合
tmpParamInfos = tmpMethod.GetParameters();
tmpParams =
new
object[tmpParamInfos.Length];
for (int i =
0; i < tmpParamInfos.Length; i++)
{
tmpParams = tmpAss.CreateInstance(tmpParamInfos.ParameterType.FullName);
if (tmpParamInfos.ParameterType.FullName ==
&quot;System.String&quot;)
{
tmpParams =
&quot;Clark Zheng&quot;;
}
if (tmpParamInfos.ParameterType.FullName ==
&quot;System.Int32&quot;)
{
tmpParams =
27;
}
}
tmpMethod.Invoke(tmpObj, tmpParams);
}
//調用完方法後再次打印對象,比較結果
Console.WriteLine(tmpObj);
}
}
Console.ReadLine();
}
}
}
結果:
Name: Clark, Age: 0
Name: Clark Zheng, Age: 27設計模式