###### tags: `自主學習`
# C# 認識物件
## ♟ 屬性 Properties
**屬性**是提供彈性機制以讀取、寫入或計算私用欄位值的成員。使用屬性時可將其視為公用資料成員,但實際上屬性是名為「存取子」的特殊方法。如此可讓資料更容易存取,同時有助於提升方法的安全性和彈性。
### set、get
**set** 搭配 **value**(存取讀取值) 用來指派新值、**get** 用來傳回屬性值。
```csharp
public class TimePeriod{
private int second;
public double GetMinute{
get{return second / 60;}
set{second = value;}
}
}
Minute t = new Minute();
t.GetMinute = 600;
Console.WriteLine($"Time in hours: {t.GetMinute}");
//Time in hours: 10
```
以上的範例就是屬性的實作,可以發現屬性相對於一般方法更加安全,因為屬性成功做到**封箱**的功能。程式中接觸到屬性的方法只有運用 set 的管道,而輸出也被管控限制於 get 出口。
**屬性**在程式功能的維護方面相當出色,因為本身與外界程式並沒有直接性的接觸,因此 Debug 、加入新功能( 在屬性區塊中加入即可 )較為容易
get 與 set 可以單獨存在,一般單獨存在 set 的類別可以將其加上 **readonly** 的修飾詞。
### 建構式呼叫黑盒
**建構式呼叫黑盒**是當我們建造物件時,直接於建構式中傳遞參數,以此簡化程式碼,也將物件本身與參數的連結更加強調。
```csharp
using System;
public class Person
{
private string _firstName;
private string _lastName;
public Person(string first, string last)
{
_firstName = first;
_lastName = last;
}
public string Name => $"{_firstName} {_lastName}";
}
public class Example
{
public static void Main()
{
var person = new Person("Magnus", "Hedlund");
Console.WriteLine(person.Name);
}
}
// The example displays the following output:
// Magnus Hedlund
```
以上的範例呈現了建造物件時,同時間設置好參數,一兼兩顧。
但我相信大家都注意到了,```=>``` 這是三毀。
#### Lambda 運算子
```=>``` 名稱就是 Lambda 運算子喔,有 ```=>``` 在的式子就是 Lambda 運算式( Lambda expression)。```=>``` 的功能可以理解為:<傳入區>```=>```<回傳值>,也類似於一個精簡函式的寫法。
```csharp
//無 Lambda expression
public square (int x)
{
return x*x;
}
Console.WriteLine(square(5));
//有 Lambda expression
Func<int, int> square = x => x * x;
// Func<T,TResult> T是泛型喔
// Func<傳入值型態,回傳值型態>
Console.WriteLine(square(5));
```
當然了,這只是最簡單的 Lambda 參數式,在更上面的範例其實用到了較為進階的手法。
```csharp
public string Name => $"{_firstName} {_lastName}";
```
這行程式碼可以理解為,當我呼叫 Name 他會直接回傳 ```=>``` 右方的值
更多關於 [**Lambda 運算子**](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions#code-try-4) 的資訊可以點連結進入參考喔。
### 自動實作屬性
自動實作屬性也就是將 get 與 set 部分程式碼省略,直接交由編譯器判斷,並將原先需要做的設定,寫在外部的程式碼中傳遞。如此一來,相對於 ```=>``` 運算子,可以在黑盒內有明確的變數或參數名稱,但也同時將黑箱打開,將其需傳遞的參數變為 public 型別了。
```csharp
public class SaleItem
{
public string Name
{ get; set; }
public decimal Price
{ get; set; }
}
class Program
{
static void Main(string[] args)
{
var item = new SaleItem{ Name = "Shoes", Price = 19.95m };
Console.WriteLine($"{item.Name}: sells for {item.Price:C2}");
}
}
// The example displays output like the following:
// Shoes: sells for $19.95
```
如果同時擁有 get、set,就必須都使用自動實作屬性。
## 🔗 函式多載 Method Overloading
其實函式多載的概念非常簡單,也就是程式能夠分辨相同名稱但是不同參數型別傳遞的函式。
```csharp
public class Example
{
public int Add(int a, int b)
{
return a + b;
}
public string Add(string a, string b)
{
return a + b;
}
}
Example.Add(1,2);
Example.Add("hello",", world!");
```
雖然名稱一樣,但是因為傳遞參數的不同,因此程式能夠判斷你呼叫的是哪個函式。
但是,假如我呼叫的函式為 : ```Example.Add(1,2,3);```
此時編譯器便會開始依照下列順序嘗試呼叫。
1. 嘗試符合參數型態但不符合參數個數的方法
2. 編譯錯誤
需要注意的一點是,編譯器在方法多載時只看參數型別選擇函式而非回傳值。
```csharp
public class Example
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(int a, int b)
{
return a + b;
}
}
```
以上的例子中,編譯器會視 2 種方法為同一種,因此造成編譯錯誤。
## 🎗 泛型 Generics
**泛型**?聽都沒聽過。但是假如你用過 List 的宣告其實已經使用到泛型的概念,只是 C# 將宣告的部分包裝起來,直接將 List 丟給我們學習。現在我們來一虧泛型的全貌吧!
### 時機
泛型的功效使我們只需宣告一種方法,而泛型會幫我們處理變數傳遞的宣告問題。
那有什麼用呢?泛型減少了程式重複宣告方法或類別只為了配合不同傳進來的參數型別而造成的累贅,也方便了程式的閱讀性更避免了造成多載的問題。
因此,當我們需要**一個會有不同參數型別傳入的方法或類型,但其功能完全相同**,這時,就是我們使用泛型的最佳時機。
### 宣告
#### 泛型方法 GenericMethod
```csharp
public int GenericMethod(int param)
{
return param;
}
GenericMethod(1) // 呼叫
```
```csharp
public T GenericMethod<T>(T param)
{
return param;
}
GenericMethod<int>(1)
```
觀察過後,會發現泛型與一般宣告並無太大歧異,將泛型區塊中所有與傳入參數型別相關之關鍵字替代為```<T>``` 中的 T (Template)。呼叫的時候於<填寫你所要傳遞的參數型別>。
#### 泛型類別 GenericClass
```csharp
public class GenericClass <T>
{
T item;
public void UpdateItem(T newItem)
{
item = newItem;
}
}
GenericClass<int> myClass = new GenericClass<int>();
myClass.UpdateItem(5);
```
運用上大同小異,呼叫時於建立時便先傳遞變數型別,之後使用其方法的時候再傳遞參數。
## 👨👦 繼承 Inheritance
**繼承**即**子類別**有**父類別**部分程式效能。
程式中,我們可以在子類別中不需要宣告特定變數或方法,就可以使用在父類別中已經宣告為 public 與 protected 的變數或方法。在最初步的理解,我們可以想成子類別的程式上面,就是父類別的程式,然後我們將類別拔掉,就變成是一般我們最熟悉的簡單程式。
觀念上,**繼承**相當強調**子類別 is a 父類別**的關係,例如:**黑面琵鷺 is a 候鳥**,**候鳥 is a 鳥**。
這觀念與將來介紹的介面 Interface 不同,需要多多注意。
### 類別 class
#### 方法宣告
+ **Public**
在其衍生類別中與外部程式中也可以使用,沒有任何限制。
+ **Private**
僅限於父類別中的程式使用,一般用於宣告於父類別有但子類別沒有的專有行為。其中,**建構式**不需要另外作 Private 的宣告,因為建構式為建構此類別的依據,且子類別會有自己的建構式,建構式建構出複數個物件於一個物件上,在邏輯上是行不通的。
+ **Protected**
介於 **Public** 與 **Private** 之間,子類別可以使用父類別中的函式,但外部不能使用。
#### 父類別 Parent/Base class
```csharp
public class Parent
{
public int speed;
public Parent
{
//Constructor will not inherited by any derived classes.
}
public void Walk()
{
}
// do something
}
```
#### 子類別 Child/Derived class
```csharp
public class Child : Parent
{
speed++; // 不須宣告即可使用父類別的變數
Walk() // 不須宣告即可使用父類別的方法
// do other things
}
```
### base 關鍵字
認識 basa 關鍵字前,我們必須重新理解繼承的相互關係。
總歸一句話,**the base class of ```Derivrdclass``` is ```Baseclass```.**
下面的例子中,the base class of ```Point3D``` is ```Point```, and the base class of ```Point``` is ```object```.
#### Overriding Constructor
```csharp
public class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
```
```csharp
public class Point3D : Point
{
public int z;
public Point3D(int x, int y, int z): base(x, y) {
this.z = z;
}
}
```
因此我們在這邊可以將存在於 Derived class 中的所有 base 關鍵字視為其 Base class 之**名字**。換句話說,這邊的```Point3D```中的```base```就形同於```Point```。
所以這段程式怎麼運行的呢?運行至 base 關鍵字後,程式將會呼叫 Bass class 中的 Constructor。執行完再回至 Derived class 中的 Constructor 中。
#### Overriding Method
```csharp
public class Person
{
protected string ssn = "444-55-6666";
protected string name = "John L. Malgraine";
public virtual void GetInfo()
{
Console.WriteLine("Name: {0}", name);
Console.WriteLine("SSN: {0}", ssn);
}
}
class Employee : Person
{
public string id = "ABC567EFG";
public override void GetInfo()
{
// Calling the base class GetInfo method:
base.GetInfo();
Console.WriteLine("Employee ID: {0}", id);
}
}
class TestClass
{
static void Main()
{
Employee E = new Employee();
E.GetInfo();
}
}
/*
Output
Name: John L. Malgraine
SSN: 444-55-6666
Employee ID: ABC567EFG
*/
```
以上的範例中,Employee class 中的 ```base.GetInfo()``` 相當於```Person.GetInfo()```,也就是呼叫在 Person class 中相同名字( overriding )的方法。
## 🎭 多形 Polymophism
**多形**是一種較為特別的繼承關係。由於一個 BassClass 可以擁有多個 DerivedClass,在某些程式中,我們很常會需要在 DerivedClass 重複執行某一特定方法或函式,這時我們就可以用多形的概念。也就是在 BassClass 中寫下該方法,再到 DerivedClass 中呼叫 BassClass 的方法。這樣一來我們就能不用每個方法重覆寫於每個 DerivedClass。
```csharp
public class Shape
{
// A few example members
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
public class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
public class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
public class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used whereever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
var shapes = new List<Shape>
{
new Rectangle(),
new Triangle(),
new Circle()
};
// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
shape.Draw();
}
/* Output:
Drawing a rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
*/
```
以上的例子便是多形的實作,我們重複呼叫了 BaseClass 的 Shape 方法,如此避免覆寫所造成的空間的累贅。
### 📨 Cast
當程式在編譯繼承時,會先創造出一個物件,再將他的型別轉由```<class>```作參考。例如 : ```Someclass classname = new Someclass()```,程式先是建出了Someclass物件再將物件轉由指派給最左邊的型別參考。
那,我們可不可以建造出一個物件再將他轉給他的Base class、Derives class 參考呢?
![](https://i.imgur.com/bmUEtFW.png)
#### 📤 Upcasting
```BaseClass classname = new DerivedClass();```
我們用白話一點的方式解釋,**我的本質是子類別,但把我當成父類別看待**。
一般來說,Upcasting 不會造成編譯上的錯誤。因為**繼承**本身有著方法繼承的特性,父類別一定有子類別的函式;換句話說,繼承強調的 **is a** 關係中,**子類別 is a 父類別** 本來就是正確的。所以當我們將子類別物件參考轉向父類別時(強制或非強制),並不會造成編譯錯誤。
```csharp
public class BaseClass
{
public void DoWork() {
Console.WriteLine("Upcasting");
}
public void Sleep() {
Console.WriteLine("Sleeping");
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() {
Console.WriteLine("Normal");
}
}
BaseClass Example = new DerivedClass();
Example.DoWork();
//Output : Upcasting
Example.Sleep();
//Output : Sleeping
```
#### 📥 Downcasting
```DerivedClass classname = new BaseClass();```
我們用白話一點的方式解釋,**我的本質是父類別,但把我當成子類別看待**。
相對於 Upacating,**Downcasting** 容易發生錯誤,因為繼承強調的是 **is a** 關係而非 **is** 的指派關係,例如:人類 is a 哺乳類⭕,但哺乳類 is a 人類❌。用最粗淺的想法,子類別比父類別多一些函式,所以比父類別強,因此有時就會發生父類別能力不足的錯誤。
```csharp
public class BaseClass
{
public void DoWork() {
Console.WriteLine("Normal");
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() {
Console.WriteLine("Downcasting");
}
public new void Sleep() {
Console.WriteLine("Sleeping");
}
}
DerivedClass Example = (DerivedClass)new BaseClass();
Example.DoWork();
//Output : Downcasting
Example.Sleep();
//Compile error
```
### 🗳 方法隱蓋 Member Hiding
若是在 DerivedClass 中宣告與 BaseClass 具有相同格式的方法,就必須在 DerivedClass 的宣告前加上 **new** 修飾詞。 new 與 overriding 是完全相反的概念,使用 new 會造成 Member Hiding,也就是目前我在哪個Class 我就優先使用的該Class的變數或是方法(Hide other methods)。
```csharp
public class Humanoid
{
//Base version of the Yell method
public void Yell()
{
Debug.Log ("Humanoid version of the Yell() method");
}
}
public class Enemy: Humanoid
{
//This hides the Humanoid version.
new public void Yell()
{
Debug.Log ("Enemy version of the Yell() method");
}
}
Enemy Example = new Humanoid();
Example.Yell();
(Humanoid)Example.Yell();// Upcasting
//Output : Enemy version of the Yell() method
//Output : Humanoid version of the Yell() method
```
:::info
所以這衍生了一個問題,**new** 修飾詞到底甚麼時候使用?
在物件導向的程式中,只要是帶有**建立新東西**(**無論是變數或方法或物件**)的意思,就可以在前面加上 new。(雖然 new 真的超少用)
其實在我們可以這樣理解平常所做的宣告:
```csharp
int a = 10;
new int a = 10;
```
這樣一來就了解了吧!
:::
### ✒ 覆寫 Overriding
事實上,在大多數情況,即使處於 Upcasting 的狀態,我們仍然希望能夠使用 DerivedClass 的方法,這時我們就可以使用 **override** 修飾詞。override 修飾詞會「延伸」基底類別的 virtual 方法,因此會優先參考 override 過後的方法 。
```csharp
public class Humanoid
{
//Base version of the Yell method
public virtual void Yell()
{
Debug.Log ("Humanoid version of the Yell() method");
}
}
public class Enemy: Humanoid
{
//This hides the Humanoid version.
public override void Yell()
{
Debug.Log ("Enemy version of the Yell() method");
}
}
Enemy Example = new Humanoid();
Example.Yell();
(Humanoid)Example.Yell();// Upcasting
//Output : Enemy version of the Yell() method
//Output : Enemy version of the Yell() method
```
如此一來我們就可以避免 Upcasting 時呼叫到 BaseClass 的方法了。但是假如我們要使用 BaseClass 呢?這時候就可以使用 **base** 關鍵字隨時呼叫,也因此 override 一般使用上比 new 還要更彈性。
:::info
在相同的成員上同時使用 **new** 和 **override** 是不正確的,因為這兩個修飾詞在意義上互斥。 new 修飾詞會以相同名稱建立新成員,並使原始成員隱藏。 override 修飾詞會擴充繼承成員的實作。
[**new 詳細資訊**](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/new-modifier)
[**override 詳細資訊**](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/override)
:::
## 👥 介面 Interfaces
說到這邊,大家都認為繼承無懈可擊了?對吧!
即使繼承相當方便,仍然是潘朵拉的盒子。
繼承過於直白,直白的可怕。
舉個例子:今天你已經實作了Fish 的 BassClass( 有 Swim()方法 ) 及其 DerivedClass 們。現在突然要加入**美人魚**這個物件,他可以繼承 Fish 嗎?呃。。。應該算還可以吧?於是你加入了美人魚 DerivedClass 。那如果是**潛艇**?他可以繼承 Fish 嗎?這未免太牽強了吧?
因此,大多數物件導向的程式語言有著**介面** **Interface** 這樣的功能。
如果說繼承是 **BClass is a AClass** 的關係,介面就是 **BClass implement AInterface** 的關係。
以上的例子中,我們將 Swim() 定義為一種介面,Fish Class、Mermaid Class 及 Submarine 一齊實作( **implement** ) Swim() 這個介面。
```csharp
//This is a basic interface with a single required
//method.
public interface IKillable
{
void Kill();
}
//This is a generic interface where T is a placeholder
//for a data type that will be provided by the
//implementing class.
public interface IDamageable<T>
{
void Damage(T damageTaken);
}
public class Avatar : MonoBehaviour, IKillable, IDamageable<float>
{
//The required method of the IKillable interface
public void Kill()
{
//Do something fun
}
//The required method of the IDamageable interface
public void Damage(float damageTaken)
{
//Do something fun
}
}
```
注意到了嗎?一般對於繼承,我們嚴格要求物件只能有一個父類別,但介面卻不受此限,因此一個物件可以實作多種介面的行為,更加彈性,也更方便加入新功能。
## 📼 擴充方法 Extension Method
**擴充方法**是一種不必建造子類別及修改到原本類別的特殊作法,一般適用在你無法直接接觸到某類別的 Source code 時,我們可以在外部**擴充**該類別的方法,再於其他地方以一般呼叫該類別其他方法使用此擴充方法。
```csharp
public static class MyExtensions
{
public static int WordCount(this String str)
//(this <要擴充的類別> 隨便一個參數名字)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
```
外部呼叫
```csharp
using MyExtensions// 加上擴充方法宣告之該類別名稱
string s = "Hello Extension Methods";
int i = s.WordCount();//以 String 類別的方法使用
```
### static 靜態類別
static是一個修飾詞,用來宣告靜態成員,可以與類別、欄位、方法、屬性、運算子、事件和建構函式搭配使用。
1. 一開始載入時就存在,就佔據記憶體
2. 不能也不用 new 就可以使用(因為已經占據記憶體了)
3. static 的成員是大家共享的(使用同一區記憶體)
當然了, static 非常一體兩面
+ 優點:
若使用在公用變數,可以共享記憶體
若使用在公用方法,大家都可以叫用
- 缺點:
無論有沒有使用,它一開始就佔據記憶體空間。
因為共享很難控制存取權限
+ 特性
- 靜態類別僅包含靜態成員
- 不能使用new實體化
- 屬密封類別,無法被繼承
- 沒有執行個體( **不需要建實體** ),只能使用私有的建構函式,或者配合靜態建構函式
- 一般方法只有該類別實體可以叫用,靜態方法使用類別名稱後大家都可以叫用( 公用 )
```csharp
public class NotStaticClass
{
public int price_1;
public void getPrice() { }
public static void getPriceStatic() { }//一般類別也可以包含靜態成員
public static price_0 = 0;
public Deposit{
get{return price_0;}
set{price_0 = value;}
}
public static class StaticClass
{
public static price = 0;
public static void getPrice() { }
}
```
#### static 變數
```csharp
StaticClass Example_1 = new StaticClass();
Example_1.Deposit = 100;
StaticClass Example_2 = new StaticClass();
Example_2.Deposit = 120;
Console.WriteLine(Example_1.Deposit);
Console.WriteLine(Example_2.Deposit);
/*Output:
120
120
*/
```
即使建造了不同物件,變數使用的記憶體空間依然共用,因此對變數值的控管要更加注意。
#### static 方法類別
non static 的方法類別需要先建立物件才能呼叫,static 的方法直接呼叫即可;因為程式一開始執行的時候就已經存在。
```csharp
// static 呼叫方法
int S = Static.price;
StaticClass.getPrice()
// non static 呼叫方法
NotStaticClass NS = new NotStaticClass();
NS.price_1 = 100;
NS.getPriceStatic();
```
### this 關鍵字
```this``` 關鍵字指的是類別的目前執行個體,也用作擴充方法第一個參數的修飾詞。
#### Constructor 建構式
this 最經典的用法便是用在建構式,this 關鍵字用來強調當前執行的**主角**,在建構式中以區分跟主角相同名稱的變數傳遞值。
```csharp
public class Employee
{
private string alias;
private string name;
public Employee(string name, string alias)
{
// Use this to qualify the members of the class
// instead of the constructor parameters.
this.name = name;
this.alias = alias;
}
}
```
#### 擴充方法
這邊是 this 的另一種用法,可以搭配 this 將物件當作參數傳遞。
```csharp
public static class MyExtensions
{
public static int WordCount(this String str)
//(this <要擴充的類別> 隨便一個參數名字)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
```
在以下的範例中,this 後面不加任何名稱即可傳遞,這是因為 this 本身就代表著該類別物件實體。
```csharp
class Employee
{
private string name;
private string alias;
private decimal salary = 3000.00m;
// Constructor:
public Employee(string name, string alias)
{
// Use this to qualify the fields, name and alias:
this.name = name;
this.alias = alias;
}
// Printing method:
public void printEmployee()
{
Console.WriteLine("Name: {0}\nAlias: {1}", name, alias);
// Passing the object to the CalcTax method by using this:
Console.WriteLine("Taxes: {0:C}", Tax.CalcTax(this));
}
public decimal Salary
{
get { return salary; }
}
}
class Tax
{
public static decimal CalcTax(Employee E)
{
return 0.08m * E.Salary;
}
}
```
#### 宣告索引
在類別中若想使用 Array 、 List 等等有關索引子 **Indexer**,就可以使用 this 來從外部呼叫。
```csharp
class UserCollection
{
Dictionary<string, User> users = new Dictionary<string, User>();
public User this[string name]
{
get => (User) users[name];
set => users[name] = value;
}
}
// using the indexer
static void Main(string[] args)
{
var users = new UserCollection();
// add objects using indexer
users["julie"] = new User("Julie Lerman", "joelin@indo.com");
users["mark"] = new User("Mark Lettuce", "mark@lettuce.com");
users["peter"] = new User("Peter Mbanugo", "p.mbanugo@yahoo.com");
// obtain and display Mark's data
Console.WriteLine($"Marks Email: {users["mark"].Email}");
Console.Read();
}
```
[**索引子宣告**](https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/indexers/using-indexers)更多詳細資料可以點連結進去看喔。
## 📁命名空間 Namespaces
**Namespace** 有點類似於 C++ 的封包,還記得我們每次在程式前都會加上```using System``` 嗎?System本身就是一種默認的封包,然後我們在程式執行時將其導入,因此可以使用裡面類別、函式等等( 如:```ConsoleWriteLine``` )
說了那麼多,所以我們可以自己建立或改寫 **Namespace** 嗎?
當然可以!
### 建立、改寫 Namespaces
建立 Namespace 其實非常簡單,以下有通式的寫法。
註解的部分是當我們需要使用,但卻沒有導入該 Namespace 時,就需要以該格式使用方法或建立物件。
+ 建立
這邊需要注意 C2 的用法,命名相同是可行的,但是其完整的名稱卻不同,總之就是要把巢狀的部分寫出來啦。
```csharp
namespace N1 // N1
{
class C1 // N1.C1
{
class C2 // N1.C1.C2
{
}
}
namespace N2 // N1.N2
{
class C2 // N1.N2.C2
{
}
}
}
```
:::info
命名空間位置:
1. ```N1``` 命名空間是全域命名空間的成員。
2. ```N2``` 命名空間是 ```N1``` 的成員。
3. ```C1``` 類別是 ```N1``` 的成員。
:::
+ 改寫
```csharp
namespace N1.N2
{
class C3 // N1.N2.C3
{
}
}
```
+ 巢狀 Namespace 建立物件
若是使用了巢狀 Namespace 並且需要建立物件,這邊建議在的宣告中將完整名稱全部寫出來,以方便 Debug,如:```N1.N2.C2 <Name> = new Ni.N2.C2()```。
不過若是因為各種理由不想寫( **懶** ),程式的 Default 是以全域的 class 為主。
```csharp
namespace SampleNamespace
{
class SampleClass
{
public void SampleMethod()
{
System.Console.WriteLine(
"SampleMethod inside SampleNamespace");
}
}
// Create a nested namespace, and define another class.
namespace NestedNamespace
{
class SampleClass
{
public void SampleMethod()
{
System.Console.WriteLine(
"SampleMethod inside NestedNamespace");
}
}
}
class Program
{
static void Main(string[] args)
{
// Displays "SampleMethod inside SampleNamespace."
SampleClass outer = new SampleClass();
outer.SampleMethod();
// Displays "SampleMethod inside SampleNamespace."
SampleNamespace.SampleClass outer2 =
new SampleNamespace.SampleClass();
outer2.SampleMethod();
// Displays "SampleMethod inside NestedNamespace."
NestedNamespace.SampleClass inner =
new NestedNamespace.SampleClass();
inner.SampleMethod();
}
}
}
```
### 使用命名空間
一般而言,使用命名空間別名限定詞 ```::``` 來參考命名空間別名,或使用 ```global::``` 來參考全域命名空間,以及使用 ```.``` 來限定型別或成員。
+ 改稱
假如全域命名空間中的一個**型別**以另一名稱參考,如:```using Alias = System.Console;```,此時使用 ```::``` 是錯誤的!
```csharp
class TestClass
{
static void Main()
{
// Error
//Alias::WriteLine("Hi");
// OK
Alias.WriteLine("Hi");
}
}
```
但若是在全域命名空間之巢狀的**命名空間**即可使用。
```csharp
using forwinforms = System.Drawing;
using forwpf = System.Windows;
public class Converters
{
public static forwpf::Point Convert(forwinforms::Point point) =>
new forwpf::Point(point.X, point.Y);
}
```
+ 專於導入命名空間( **避免導入非預期類型** )
```::``` 限定詞可確保其左邊的識別字一律會參考**命名空間(別名)**,即使存在具有相同名稱的型別也一樣。
此時使用 ```::``` 是為了使被改稱之全域的 System 被視為命名空間的改稱,而非型別。
```csharp
using Alias = System;
namespace Library
{
public class C : Alias::Exception { }
}
```
+ 結論
使用```::```的情況為目的型強調右邊之識別字為**左側的命名空間**(若於全域則使用 global)識別字的方法、型別等等。