1. 程式人生 > >C#基礎概念二十五問[轉]

C#基礎概念二十五問[轉]

注:本文部份資料來自網路,如有侵權,請與我聯絡,我會在第一時間宣告引用或將其刪除!

    當初學 C# 時是找個人大概問了一下資料型別和分支語句就開始做專案了。這兩天又全面的看了一下相關的基礎知識(學而時習之嘛),總結了25個問題:

1.靜態成員和非靜態成員的區別?
2.const 和 static readonly 區別?
3.extern 是什麼意思?
4.abstract 是什麼意思?
5.internal 修飾符起什麼作用?
6.sealed 修飾符是幹什麼的?
7.override 和 overload 的區別?
8.什麼是索引指示器?
9.new 修飾符是起什麼作用?
10.this 關鍵字的含義?
11.可以使用抽象函式重寫基類中的虛擬函式嗎?
12.密封類可以有虛擬函式嗎?
13.什麼是屬性訪問器?
14.abstract 可以和 virtual 一起使用嗎?可以和 override 一起使用嗎?
15.介面可以包含哪些成員?
16.類和結構的區別?
17.介面的多繼承會帶來哪些問題?
18.抽象類和介面的區別?
19.別名指示符是什麼?
20.如何手工釋放資源?
21.P/Invoke是什麼?
22.StringBuilder 和 String 的區別?
23.explicit 和 implicit 的含義?
24.params 有什麼用?
25.什麼是反射?

以下是我做的一份參考答案(C# 語言範疇之內),如果有不準確、不全面的,歡迎各位朋友指正!

 
1.靜態成員和非靜態成員的區別?

答:

靜態變數使用 static 修飾符進行宣告,在類被例項化時建立,通過類進行訪問

不帶有 static 修飾符宣告的變數稱做非靜態變數,在物件被例項化時建立,通過物件進行訪問

一個類的所有例項的同一靜態變數都是同一個值,同一個類的不同例項的同一非靜態變數可以是不同的值

靜態函式的實現裡不能使用非靜態成員,如非靜態變數、非靜態函式等

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example01
{
    class Program
    {
        class Class1
        {
            public static String staticStr = "Class";
            public String notstaticStr = "Obj";
        }
        static void Main(string[] args)
        {
            //靜態變數通過類進行訪問,該類所有例項的同一靜態變數都是同一個值
            Console.WriteLine("Class1's staticStr: {0}"
, Class1.staticStr);
            Class1 tmpObj1 = new Class1();
            tmpObj1.notstaticStr = "tmpObj1";
            Class1 tmpObj2 = new Class1();
            tmpObj2.notstaticStr = "tmpObj2";
            //非靜態變數通過物件進行訪問,不同物件的同一非靜態變數可以有不同的值
            Console.WriteLine("tmpObj1's notstaticStr: {0}", tmpObj1.notstaticStr);
            Console.WriteLine("tmpObj2's notstaticStr: {0}", tmpObj2.notstaticStr);
            Console.ReadLine();
        }
    }
}

結果:
Class1's staticStr: Class
tmpObj1's notstaticStr: tmpObj1
tmpObj2's notstaticStr: tmpObj2


2.const 和 static readonly 區別?

答:

const

用 const 修飾符宣告的成員叫常量,是在編譯期初始化並嵌入到客戶端程式

static readonly

用 static readonly 修飾符宣告的成員依然是變數,只不過具有和常量類似的使用方法:通過類進行訪問、初始化後不可以修改。但與常量不同的是這種變數是在執行期初始化

示例:

測試類:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example02Lib
{
    public class Class1
    {
        public const String strConst = "Const";
        public static readonly String strStaticReadonly = "StaticReadonly";
        //public const String strConst = "Const Changed";
        //public static readonly String strStaticReadonly = "StaticReadonly Changed";
    }
}
客戶端程式碼:
using System;
using System.Collections.Generic;
using System.Text;
using Example02Lib;
namespace Example02
{
    class Program
    {
        static void Main(string[] args)
        {
            //修改Example02中Class1的strConst初始值後,只編譯Example02Lib專案
            //然後到資源管理器裡把新編譯的Example02Lib.dll拷貝Example02.exe所在的目錄,執行Example02.exe
            //切不可在IDE裡直接除錯執行因為這會重新編譯整個解決方案!!
            //可以看到strConst的輸出沒有改變,而strStaticReadonly的輸出已經改變
            //表明Const變數是在編譯期初始化並嵌入到客戶端程式,而StaticReadonly是在執行時初始化的
            Console.WriteLine("strConst : {0}", Class1.strConst);
            Console.WriteLine("strStaticReadonly : {0}", Class1.strStaticReadonly);
            Console.ReadLine();
        }
    }
}

結果:
strConst : Const
strStaticReadonly : StaticReadonly

修改後的示例:

測試類:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example02Lib
{
    public class Class1
    {
        //public const String strConst = "Const";
        //public static readonly String strStaticReadonly = "StaticReadonly";
        public const String strConst = "Const Changed";
        public static readonly String strStaticReadonly = "StaticReadonly Changed";
    }
}

結果

strConst : Const
strStaticReadonly : StaticReadonly Changed


3.extern 是什麼意思?

答:

extern 修飾符用於宣告由程式集外部實現的成員函式

經常用於系統API函式的呼叫(通過 DllImport )。注意,和DllImport一起使用時要加上 static 修飾符

也可以用於對於同一程式集不同版本元件的呼叫(用 extern 宣告別名)

不能與 abstract 修飾符同時使用

示例:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace Example03
{
    class Program
    {
        //注意DllImport是一個Attribute Property,在System.Runtime.InteropServices名稱空間中定義
        //extern與DllImport一起使用時必須再加上一個static修飾符
        [DllImport("User32.dll")]
        public static extern int MessageBox(int Handle, string Message, string Caption, int Type);
        static int Main()
        {
            string myString;
            Console.Write("Enter your message: ");
            myString = Console.ReadLine();
            return MessageBox(0, myString, "My Message Box", 0);
        }
    }
}

結果:


4.abstract 是什麼意思?

答:

abstract 修飾符可以用於類、方法、屬性、事件和索引指示器(indexer),表示其為抽象成員

abstract 不可以和 static 、virtual 一起使用

宣告為 abstract 成員可以不包括實現程式碼,但只要類中還有未實現的抽象成員(即抽象類),那麼它的物件就不能被例項化,通常用於強制繼承類必須實現某一成員

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example04
{
    #region 基類,抽象類
    public abstract class BaseClass
    {
        //抽象屬性,同時具有get和set訪問器表示繼承類必須將該屬性實現為可讀寫
        public abstract String Attribute
        {
            get;
            set;
        }
        //抽象方法,傳入一個字串引數無返回值
        public abstract void Function(String value);
        //抽象事件,型別為系統預定義的代理(delegate):EventHandler
        public abstract event EventHandler Event;
        //抽象索引指示器,只具有get訪問器表示繼承類必須將該索引指示器實現為只讀
        public abstract Char this[int Index]
        {
            get;
        }
    }
    #endregion
    #region 繼承類
    public class DeriveClass : BaseClass
    {
        private String attribute;
        public override String Attribute
        {
            get
            {
                return attribute;
            }
            set
            {
                attribute = value;
            }
        }
        public override void Function(String value)
        {
            attribute = value;
            if (Event != null)
            {
                Event(this, new EventArgs());
            }
        }
        public override event EventHandler Event;
        public override Char this[int Index]
        {
            get
            {
                return attribute[Index];
            }
        }
    }
    #endregion
    class Program
    {
        static void OnFunction(object sender, EventArgs e)
        {
            for (int i = 0; i < ((DeriveClass)sender).Attribute.Length; i++)
            {
                Console.WriteLine(((DeriveClass)sender)[i]);
            }
        }
        static void Main(string[] args)
        {
            DeriveClass tmpObj = new DeriveClass();
            tmpObj.Attribute = "1234567";
            Console.WriteLine(tmpObj.Attribute);
            //將靜態函式OnFunction與tmpObj物件的Event事件進行關聯
            tmpObj.Event += new EventHandler(OnFunction);
            tmpObj.Function("7654321");
            Console.ReadLine();
        }
    }
}

結果:
1234567
7
6
5
4
3
2
1


5.internal 修飾符起什麼作用?

答:

internal 修飾符可以用於型別或成員,使用該修飾符宣告的型別或成員只能在同一程集內訪問

介面的成員不能使用 internal 修飾符

示例

Example05Lib 專案的 Class1

using System;
using System.Collections.Generic;
using System.Text;
namespace Example05Lib
{
    public class Class1
    {
        internal String strInternal = null;
        public String strPublic;
    }
}

結果
Example05Lib 專案的 Class2 類可以訪問到 Class1 的 strInternal 成員

Example05 專案的 Program 類無法訪問到 Class1 的 strInternal 成員


6.sealed 修飾符是幹什麼的?

答:

sealed 修飾符表示密封

用於類時,表示該類不能再被繼承,不能和 abstract 同時使用,因為這兩個修飾符在含義上互相排斥

用於方法和屬性時,表示該方法或屬性不能再被繼承,必須和 override 關鍵字一起使用,因為使用 sealed 修飾符的方法或屬性肯定是基類中相應的虛成員

通常用於實現第三方類庫時不想被客戶端繼承,或用於沒有必要再繼承的類以防止濫用繼承造成層次結構體系混亂

恰當的利用 sealed 修飾符也可以提高一定的執行效率,因為不用考慮繼承類會重寫該成員

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example06
{
    class Program
    {
        class A
        {
            public virtual void F()
            {
                Console.WriteLine("A.F");
            }
            public virtual void G()
            {
                Console.WriteLine("A.G");
            }
        }
        class B : A
        {
            public sealed override void F()
            {
                Console.WriteLine("B.F");
            }
            public override void G()
            {
                Console.WriteLine("B.G");
            }
        }
        class C : B
        {
            public override void G()
            {
                Console.WriteLine("C.G");
            }
        }
        static void Main(string[] args)
        {
            new A().F();
            new A().G();
            new B().F();
            new B().G();
            new C().F();
            new C().G();
            Console.ReadLine();
        }
    }
}

結果:
類 B 在繼承類 A 時可以重寫兩個虛擬函式,如圖所示:

由於類 B 中對 F 方法進行了密封, 類 C 在繼承類 B 時只能重寫一個函式,如圖所示:

控制檯輸出結果,類 C 的方法 F 只能是輸出 類B 中對該方法的實現:

A.F
A.G
B.F
B.G
B.F
C.G


7.override 和 overload 的區別?

答:

override 表示重寫,用於繼承類對基類中虛成員的實現

overload 表示過載,用於同一個類中同名方法不同引數(包括型別不同或個數不同)的實現

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example07
{
    class Program
    {
        class BaseClass
        {
            public virtual void F()
            {
                Console.WriteLine("BaseClass.F");
            }
        }
        class DeriveClass : BaseClass
        {
            public override void F()
            {
                base.F();
                Console.WriteLine("DeriveClass.F");
            }
            public void Add(int Left, int Right)
            {
                Console.WriteLine("Add for Int: {0}", Left + Right);
            }
            public void Add(double Left, double Right)
            {
                Console.WriteLine("Add for int: {0}", Left + Right);
            }
        }
        static void Main(string[] args)
        {
            DeriveClass tmpObj = new DeriveClass();
            tmpObj.F();
            tmpObj.Add(1, 2);
            tmpObj.Add(1.1, 2.2);
            Console.ReadLine();
        }
    }
}

結果:
BaseClass.F
DeriveClass.F
Add for Int: 3
Add for int: 3.3


8.什麼是索引指示器?

答:

實現索引指示器(indexer)的類可以象陣列那樣使用其例項後的物件,但與陣列不同的是索引指示器的引數型別不僅限於int

簡單來說,其本質就是一個含引數屬性

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example08
{
    public class Point
    {
        private double x, y;
        public Point(double X, double Y)
        {
            x = X;
            y = Y;
        }
        //重寫ToString方法方便輸出
        public override string ToString()
        {
            return String.Format("X: {0} , Y: {1}", x, y);
        }
    }
    public class Points
    {
        Point[] points;
        public Points(Point[] Points)
        {
            points = Points;
        }
        public int PointNumber
        {
            get 
            { 
                return points.Length; 
            }
        }    
        //實現索引訪問器
        public Point this[int Index]
        {
            get
            {
                return points[Index];
            }
        }
    }
    //感謝watson hua(http://huazhihao.cnblogs.com/)的指點
    //索引指示器的實質是含參屬性,引數並不只限於int
    class WeatherOfWeek
    {
        public string this[int Index]
        {
            get
            {
                //注意case段使用return直接返回所以不需要break
                switch (Index)
                {
                    case 0:
                        {
                            return "Today is cloudy!";
                        }
                    case 5:
                        {
                            return "Today is thundershower!";
                        }
                    default:
                        {
                            return "Today is fine!";
                        }
                }
            }
        }
        public string this[string Day]
        {
            get
            {
                string TodayWeather = null;
                //switch的標準寫法
                switch (Day)
                {
                    case "Sunday":
                        {
                            TodayWeather = "Today is cloudy!";
                            break;
                        }
                    case "Friday":
                        {
                            TodayWeather = "Today is thundershower!";
                            break;
                        }
                    default:
                        {
                            TodayWeather = "Today is fine!";
                            break;
                        }
                }
                return TodayWeather;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Point[] tmpPoints = new Point[10];
            for (int i = 0; i < tmpPoints.Length; i++)
            {
                tmpPoints[i] = new Point(i, Math.Sin(i));
            }
            Points tmpObj = new Points(tmpPoints);
            for (int i = 0; i < tmpObj.PointNumber; i++)
            {
                Console.WriteLine(tmpObj[i]);
            }
            string[] Week = new string[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Staurday"};
            WeatherOfWeek tmpWeatherOfWeek = new WeatherOfWeek();
            for (int i = 0; i < 6; i++)
            {
                Console.WriteLine(tmpWeatherOfWeek[i]);
            }
            foreach (string tmpDay in Week)
            {
                Console.WriteLine(tmpWeatherOfWeek[tmpDay]);
            }
            Console.ReadLine();
        }
    }
}

結果:
X: 0 , Y: 0
X: 1 , Y: 0.841470984807897
X: 2 , Y: 0.909297426825682
X: 3 , Y: 0.141120008059867
X: 4 , Y: -0.756802495307928
X: 5 , Y: -0.958924274663138
X: 6 , Y: -0.279415498198926
X: 7 , Y: 0.656986598718789
X: 8 , Y: 0.989358246623382
X: 9 , Y: 0.412118485241757
Today is cloudy!
Today is fine!
Today is fine!
Today is fine!
Today is fine!
Today is thundershower!
Today is cloudy!
Today is fine!
Today is fine!
Today is fine!
Today is fine!
Today is thundershower!
Today is fine!


9.new 修飾符是起什麼作用?

答:

new 修飾符與 new 操作符是兩個概念

new 修飾符用於宣告類或類的成員,表示隱藏了基類中同名的成員。而new 操作符用於例項化一個型別

new 修飾符只能用於繼承類,一般用於彌補基類設計的不足

new 修飾符和 override 修飾符不可同時用在一個成員上,因為這兩個修飾符在含義上互相排斥

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example09
{
    class BaseClass
    {
        //基類設計者聲明瞭一個PI的公共變數,方便進行運算
        public static double PI = 3.1415;
    }
    class DervieClass : BaseClass
    {
        //繼承類發現該變數的值不能滿足運算精度,於是可以通過new修飾符顯示隱藏基類中的宣告
        public new static double PI = 3.1415926;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(BaseClass.PI);
            Console.WriteLine(DervieClass.PI);
            Console.ReadLine();
        }
    }
}

結果:
3.1415
3.1415926


10.this 關鍵字的含義?

答:

this 是一個保留字,僅限於建構函式和方法成員中使用

在類的建構函式中出現表示對正在構造的物件本身的引用,在類的方法中出現表示對呼叫該方法的物件的引用,在結構的構造上函式中出現表示對正在構造的結構的引用,在結構的方法中出現表示對呼叫該方法的結果的引用

this 保留字不能用於靜態成員的實現裡,因為這時物件或結構並未例項化

在 C# 系統中,this 實際上是一個常量,所以不能使用 this++ 這樣的運算

this 保留字一般用於限定同名的隱藏成員、將物件本身做為引數、宣告索引訪問器、判斷傳入引數的物件是否為本身

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example10
{
    class Class1
    {
        private double c;
        private string value;
        public double C
        {
            get
            {
                return c;
            }
        }
        public Class1(double c)
        {
            //限定同名的隱藏成員
            this.c = c;
        }
        public Class1(Class1 value)
        {
            //用物件本身例項化自己沒有意義
            if (this != value)
            {
                c = value.C;
            }
        }
        public override string ToString()
        {
            //將物件本身做為引數
            return string.Format("{0} Celsius = {1} Fahrenheit", c, UnitTransClass.C2F(this));
        }
        //由於好奇,在這做了一個效率測試,想看看到底哪種方式訪問成員變數更快,結論:區別不大。。。
        public string Test1()
        {
            long vTickCount = Environment.TickCount;
            for (int i = 0; i < 10000000; i++)
                this.value = i.ToString();
            return string.Format("Have this.: {0} MSEL", Environment.TickCount - vTickCount);
        }
        public string Test2()
        {
            long vTickCount = Environment.TickCount;
            for (int i = 0; i < 10000000; i++)
                value = i.ToString();
            return string.Format("Don't have this.: {0} MSEL", Environment.TickCount - vTickCount);
        }
    }
    class UnitTransClass
    {
        public static double C2F(Class1 value)
        {
            //攝氏到華氏的轉換公式
            return 1.8 * value.C + 32;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Class1 tmpObj = new Class1(37.5);
            Console.WriteLine(tmpObj);
            Console.WriteLine(tmpObj.Test1());
            Console.WriteLine(tmpObj.Test2());
            Console.ReadLine();
        }
    }
}

結果:
37.5 Celsius = 99.5 Fahrenheit
Have this.: 4375 MSEL
Don't have this.: 4406 MSEL


11.可以使用抽象函式重寫基類中的虛擬函式嗎?

答:

可以

需使用 new 修飾符顯式宣告,表示隱藏了基類中該函式的實現

或增加 override 修飾符,表示抽象重寫了基類中該函式的實現

示例:

    class BaseClass
    {
        public virtual void F()
        {
            Console.WriteLine("BaseClass.F");
        }
    }
    abstract class  DeriveClass1 : BaseClass
    {
        public abstract new void F();
    }
    //感謝watson hua(http://huazhihao.cnblogs.com/)的指點
    //是他提醒了我還可以用這種方法抽象重寫基類的虛方法
    abstract class DeriveClass2 : BaseClass
    {
        public abstract override 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.什麼是屬性訪問器?

答:

屬性訪問器(Property Accessor),包括 get 訪問器和 set 訪問器分別用於欄位的讀寫操作

其設計目的主要是為了實現面向物件(OO)中的封裝思想。根據該思想,欄位最好設為private,一個精巧的類最好不要直接把欄位設為公有提供給客戶呼叫端直接訪問

另外要注意屬性本身並不一定和欄位相聯絡


14.abstract 可以和 virtual 一起使用嗎?可以和 override 一起使用嗎?

答:

abstract 修飾符不可以和 static、virtual 修飾符一起使用

abstract 修飾符可以和 override 一起使用,參見第11點

示例:

using System;
using System.Collections.Generic;
using System.Text;
namespace Example14
{
    class BaseClass
    {
        public virtual void F()
        {
            Console.WriteLine("BaseClass.F");
        }
    }
    abstract class DeriveClass1 : BaseClass
    {
        //在這裡, abstract是可以和override一起使用的
        public abstract override void F();
    }
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}


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.別名指示符是什麼?

答:

通過別名指示符我們可以為某個型別起一個別名

主要用於解決兩個名稱空間內有同名型別的衝突或避免使用冗餘的名稱空間

別名指示符只在一個單元檔案內起作用