C#筆記(1)-基礎知識

[TOC]

  • 一 前言
  • 二 關鍵字
  • 三 Hello World!
  • 四 預定義類型
    • 1. 數值
      • 獲取類型,類型轉換,檢查溢出
      • 浮點類型
    • 2 字符串和字符
    • 3 數組
      • 值類型
      • 引用類型
    • 4 多維數組
    • 5 ref 修飾符
    • 6 out 修飾符
    • 7 params 修飾符
    • 8 可選參數
    • 9 命名參數
    • 10 運算符
      • null 相關運算符
    • 11 類型 switch
    • 12 foreach
  • 四 創建類型
    • 1 字段
    • 2 方法
      • 解構器
    • 3 屬性
    • 4 常量
    • 5 終結器
    • 6 分部類型和方法
    • 7 object 類型
    • 8 裝箱和拆箱
    • 9 結構體
    • 10 訪問權限修飾符
    • 11 接口
    • 12 枚舉
    • 13 泛型

一 前言

C# 是類型安全的面向對象的精妙語言,可幫助開發者生成在 .NET 生態系統中運行的各種安全可靠的應用程序。C# 可用於創建 Windows 客戶端應用程序、XML Web service、分佈式組件、客戶端服務器應用程序、數據庫應用程序等。

棧是存儲局部變量和參數的內存塊。堆是保存對象(例如引用類型的實例)的內存塊。

二 關鍵字

C# 關鍵字是預定義的保留標識符。在前面加 @ 前綴才能用作標誌符:

abstract,as,base,bool,break,byte,case,catch,char,checked,class,const,continue,decimal,default,delegate,do,double,else,enum,event,explicit,extern,false,finally,fixed,float,for,foreach,goto,if,implicit,in,int,interface,internal,is,lock,long,namespace,new,null,object,operator,out,override,params,private,protected,public,readonly,ref,return,sbyte,sealed,short,sizeof,stackalloc,static,string,struct,switch,this,throw,true,try,typeof,uint,ulong,unchecked,unsafe,ushort,using,virtual,void,volatile,while     

下面是上下文關鍵字,上下文關鍵字用於在代碼中提供特定含義,但它不是 C# 中的保留字:

add,alias,ascending,async,await,by,descending,dynamic,equals,from,get,global,group,into,join,let,nameof,notnull,on,orderby,partial(類型),partial(方法),remove,select,set,unmanaged(泛型類型約束),value,var,when(篩選條件),where(泛型類型約束),where(查詢子句),yield

三 Hello World!

using System;

// 生成exe命令: dotnet publish -r win10-x64 /p:PublishSingleFile=true
// namespace 命名空間在一個命名空間中聲明的類的名稱與另一個命名空間中聲明的相同的類的名稱不衝突。
namespace FristApp
{
    class Program
    {
        /**
         * 程序執行的默認入口點
         */
        static void Main(string[] args)
        {
            // 控制檯輸出字符串 "Hello World!"
            Console.WriteLine("Hello World!");
            
            // 聲明 int 類型變量x 並初始化
            int x = 12 * 30;
            Console.WriteLine(x);
            // 修改x的值
            x = 73;
            Console.WriteLine(x);

            // 聲明一個常量, 常量的值不能修改
            const int y = 360;
            Console.WriteLine(333 + y.ToString()); // 333360


            // 調用 FeetToInches 方法並輸出
            Console.WriteLine(FeetToInches(12));

            // User 實例
            var user = new User(22, "afra55");
            Console.WriteLine(user.SingASong());
            user.age = 133;
            // 引用類型變量賦值只會複製引用
            var user2 = user;
            user2.age = 33;
            user2.name = "Bfra55";
            Console.WriteLine(user2.SingASong());
            Console.WriteLine(user.SingASong());
            
            // 值類型實例
            var myStruct = new MyStruct();
            myStruct.age = 22;
            Console.WriteLine(myStruct.age); // 22
            // 值類型賦值是值的複製而不是引用
            var myStruct2 = myStruct;
            myStruct2.age = 100;
            Console.WriteLine(myStruct2.age); // 100
            Console.WriteLine(myStruct.age); // 22

        }

        static int FeetToInches(int feet)
        {
            int inches = feet * 12;
            return inches;
        }
    }

    // 自定義類型
    class User
    {
        public int age;
        public string name;

        /**
         * 構造方法
         */
        public User(int age, string name)
        {
            this.age = age;
            this.name = name;
        }

        // public 將方法公開
        public string SingASong()
        {
            return $"{name}({age}) sing a song {"{}"}";
        }
    }

    // struct 定義了值類型
    public struct MyStruct
    {
        public int age;
    }

}

四 預定義類型

都在 System 命名空間下

值類型:

  1. 數值
  2. 有符號整數(sbyte、short、int、long)
  3. 無符號整數(byte、ushort、uint、ulong)
  4. 實數(float、double、decimal)
  5. 邏輯值(bool)
  6. 字符(char)

引用類型:

  1. 字符串(string)
  2. 對象(object)

1. 數值

整數:

C#類型/關鍵字 範圍 大小 .NET 類型 後綴
sbyte -128到127 8位帶符號整數 System.SByte
byte 0到255 無符號的 8 位整數 System.Byte
short -32,768 到 32,767 有符號 16 位整數 System.Int16
ushort 0 到 65,535 無符號 16 位整數 System.UInt16
int -2,147,483,648 到 2,147,483,647 帶符號的 32 位整數 System.Int32
uint 0 到 4,294,967,295 無符號的 32 位整數 System.UInt32 U
long -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 64 位帶符號整數 System.Int64 L
ulong 0 到 18,446,744,073,709,551,615 無符號 64 位整數 System.UInt64 UL

實數:

C#類型/關鍵字 範圍 大小 .NET 類型 後綴
float ±1.5 x 10−45 至 ±3.4 x 1038 大約 6-9 位數字 4 個字節 System.Single F
double ±5.0 × 10−324 到 ±1.7 × 10308 大約 15-17 位數字 8 個字節 System.Double D
decimal ±1.0 x 10-28 至 ±7.9228 x 1028 28-29 位 16 個字節 System.Decimal M

獲取類型,類型轉換,檢查溢出

            // 獲取類型
            Console.WriteLine(1.1.GetType()); // System.Double
            Console.WriteLine(1.GetType()); // System.Int32

            var a = 1.1;
            // 類型轉換
            var b = (int) a;
            Console.WriteLine(b); // 1

            var c = int.MinValue;
            var d = c - 1; // 整數溢出,不會拋出異常,超出部分的進位被丟棄
            Console.WriteLine(d == int.MaxValue); // true

            try
            {
                var e = 1000000;
                var f = 1000000;
                // checked 會檢查到溢出時拋出異常
                // Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow.
                // checked 對  double 和 float 類型無作用
                int g = checked(e * f);
                Console.WriteLine(g);
                // checked 可以對整個代碼塊進行檢測
                checked
                {
                    // do something
                    unchecked
                    {
                        // 在這裏不檢測溢出
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            
            Console.WriteLine("end"); 

Out:

System.Double
System.Int32
1
True
System.OverflowException: Arithmetic operation resulted in an overflow.
   at Sceond.Project.test() in /Users/victor/Program/RiderProject/Solution1/ConsoleApp1/ConsoleApp1/Second.cs:line 27
end

浮點類型

double:


    public const double MinValue = -1.7976931348623157E+308;
    public const double MaxValue = 1.7976931348623157E+308;
    public const double Epsilon = 5E-324;
    public const double NegativeInfinity = -1.0 / 0.0;
    public const double PositiveInfinity = 1.0 / 0.0;
    public const double NaN = 0.0 / 0.0;

float:

    public const float MinValue = -3.4028235E+38f;
    public const float Epsilon = 1E-45f;
    public const float MaxValue = 3.4028235E+38f;
    public const float PositiveInfinity = 1.0f / 0.0f;
    public const float NegativeInfinity = -1.0f / 0.0f;
    public const float NaN = 0.0f / 0.0f;
    Console.WriteLine(-1.0 / 0.0); // -Infinity
    Console.WriteLine(1.0 / 0.0); // Infinity
    Console.WriteLine(0.0 / 0.0); // NaN
    Console.WriteLine(Double.NaN == Double.NaN);  // false

double類型經常用於科學計算(例如計算空間座標)。
decimal經常用於金融計算和計算那些“人爲”的而非真實世界度量。

2 字符串和字符

            Console.WriteLine( '\'');
            Console.WriteLine( '\"');
            Console.WriteLine( '\\');
            // \0 空(null)
            Console.WriteLine( '\a'); // 警告
            Console.WriteLine( '\b'); // 退格
            Console.WriteLine( '\f'); // 走紙
            Console.WriteLine( '\n'); // 換行
            Console.WriteLine( '\r'); // 回車
            Console.WriteLine( '\t'); // 水平製表符號
            Console.WriteLine( '\v'); // 垂直製表符
            
            string a = "Hello";
            string b = "Hello";
            Console.WriteLine(a == b); // True
            Console.Write("a\n"); // 輸出 a 並回車
            // @ 輸出的字符串不支持轉義字符,支持多行輸入
            Console.Write(@"a\n"); // 輸出 a\n
            Console.WriteLine("\n" + "Balala");

            int number = 56;
            // $ 和 {} 能將表達式放到字符串中計算, 並將結果轉換爲字符串
            Console.WriteLine($"number = {number + 2}");

3 數組

            // 字符集合
            char[] a = new char[5];
            // 集合默認是按位取0的內存表示的值,比如 int 集合,元素默認值就是0
            Console.WriteLine(a[0]);
            a[0] = '1';
            a[1] = '2';
            a[2] = '3';
            a[3] = '4';
            a[4] = '5';
            Console.WriteLine(a[0]);
            // for 循環遍歷每個元素
            for (int i = 1; i < a.Length; i++)
            {
                Console.WriteLine(a[i]);
            }
            // 初始化字符集合
            char[] b = new char[] {'0', '1', '2', '3', '4'};
            char[] c = {'0', '1', '2', '3', '4'};

不論元素是何種類型,數組本身總是引用類型對象。

值類型

    public struct ValueClass
    {
        public int a, b;
    }
    // -----
    // 值類型
    ValueClass[] vArr = new ValueClass[100];
    Console.WriteLine(vArr[0].a); // 0

引用類型

    public class ReferClass
    {
        public int a, b; 
    }
    // -----
       // 引用類型
            ReferClass[] rArr = new ReferClass[100];
            // 沒有初始化就使用會拋出異常
            Console.WriteLine(rArr[0].a); // Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

4 多維數組

            // 二維矩陣數組, 3 * 3
            int[,] matrix = new int[3, 3];
            for (int i = 0; i < matrix.GetLength(0); i++)
            {
                for (int j = 0; j < matrix.GetLength(1); j++)
                {
                    matrix[i, j] = i * j;
                }
            }


            // 三維矩陣數組, 3 * 3 * 3
            int[,,] matrix2 = new int[3, 3, 3]
            {
                {
                    {0, 1, 2},
                    {3, 4, 5},
                    {6, 7, 8}
                },
                {
                    {0, 1, 2},
                    {3, 4, 5},
                    {6, 7, 8}
                },
                {
                    {0, 1, 2},
                    {3, 4, 5},
                    {6, 7, 8}
                }
            };

            // 鋸齒形數組
            int[][] m = new int[3][];
            for (int i = 0; i < m.Length; i++)
            {
                m[i] = new int[3];
                for (int j = 0; j < m[i].Length; j++)
                {
                    m[i][j] = i * 3 + j;
                }
            }

            int[][] m2 = new int[][]
            {
                new int[] {0, 1, 2},
                new int[] {3, 4, 5},
                new int[] {6, 7, 9}
            };

5 ref 修飾符

        /**
         * ref 修飾符指按引用傳遞參數, p 和傳入的參數指向同一內存位置
         */
        static void Foo(ref int p)
        {
            p = p + 1; 
            Console.WriteLine(p);
        }

       public static void Test6()
        {
            var x = 9; 
            Foo(ref x); // 把 x 的引用傳進去進行修改,x的值也會變化
            Console.WriteLine(x);  // 10
        }

6 out 修飾符

out 修飾符永雨獲得方法的多個返回值,參數必須在方法結束之前賦值;
out 參數也是引用傳遞;

       static void Split(string name, out string firstName, out string lastName)
       {
           int i = name.LastIndexOf(' ');
           firstName = name.Substring(0, i);
           lastName = name.Substring(i + 1);
       }
       public static void Test7()
       {
           string a, b;
           Split("Victor afra55", out a, out b);
           Console.WriteLine(a); //Victor
           Console.WriteLine(b); // afra55
           
           // 可以使用 _ 丟棄參數
           Split("Bfra afra55", out a, out _);
           Console.WriteLine(a); // Bfra
       }

7 params 修飾符

params參數修飾符只能修飾方法中的最後一個參數,它能夠使方法接受任意數量的指定類型參數。

       static void Sum(out int sum, params int[] ints)
       {
           sum = 0;
           for (int i = 0; i < ints.Length; i++)
           {
               sum += ints[i];
           }
       }
       
       public static void Test8()
       {
           // 可以直接傳入數字
           Sum(out int sum, 1, 2, 3, 4, 5, 65); // 80
           Console.WriteLine(sum);
           
           // 可以傳入一個數組
           Sum(out int sum2, new int[]{2, 3, 4, 5, 6, 1109});
           Console.WriteLine(sum2); // 1129
       }

8 可選參數

 // x 提供默認值 321
 void Foo(int x = 321)
       {
            Console.WriteLine(x); 
       }

可選參數在調用方法的時候可以省略。
Foo(); // 321

9 命名參數

可以通過如下形式改變傳入參數的順序,按位置傳遞的參數,必須在命名參數之前。

       static void Foo(int x = 321, int y = 333)
       {
           Console.WriteLine(x + y);
       }

       public static void Test9()
       {
           Foo(y:23, x:2); // 25
           Foo(y:23);  // 344
       }

10 運算符

符號 描述 可重載|
. 成員訪問 x.y
-> 結構體指針 x->y
() 函數調用 x() 否|
|[]|數組/索引 a[x]|通過索引器
++ 後自增 x++, 前自增 ++x
-- 後自減 x--, 前自減 --x
new 創建實例 new Foo()
stackalloc 不安全的棧空間分配 stackalloc(11)
typeof 從標誌符中獲得類型 typeof(int)
nameof 從標誌服中獲得名稱 nameof(x)
checked 檢測整數溢出 checked(x)
unchecked 不檢測整數溢出 unchecked(x)
default 默認值 default(char) 否|
await 等待異步操作 await myTask
sizeof 獲得結構體的大小 sizeof(int)
?. null 條件運算符 x?.y
+ 正數 +x
- 負數 -x
! 非 !x
~ 按位求反 ~x
() 轉換 (int)x
* 取地址中的值,不安全 *x
& 取值的地址,不安全 &x
* 乘法 x * y
/ 除 x/y
% 取餘 x%y
+ 加 x+y
- 減 x-y
<< 左移 x<<1
>> 右移 x>>1
< 小於 x<y
> 大於 x>y
<= 小於等於 x<=y
>= 大於等於 x>=y
is 類型是或是子類 x is y
as 類型轉換 x as y
== 相等 x==y
!= 不相等 x!=y
& 與 x&y
^ 異或 x^y
` ` 或 `x y`
&& 條件與 x&&y 通過&
` ` 條件或`x y` 通過` `
?? null合併運算符 x??y
?: 條件運算符
= 賦值 x=y
*= 自乘 x*=4 通過*
/= 自除 x/=4 通過/
+= 自加 x+=4 通過+
-= 自減 x-=4 通過-
<<= 自身左移 x<<=2 通過<<
>>= 自身右移 x>>=2 通過>>
&= 自身與 x&=2 通過&
^= 自身異或 x^=2 通過^
` =` 自身或 `x =2` `通過 `
=> Lambda x => x + 1

null 相關運算符

           string s1 = null;
           // 左側表達式是 null 的時候,右側表達式纔會進行
           string s2 = s1 ?? "afra55";
           Console.WriteLine(s2); // afra55
           string s3 = s2 ?? "bfra55";
           Console.WriteLine(s3); // afra55

           string s4 = null;
           // 當運算符?的左側爲null的時候,該表達式的運算結果也是null而不會拋出NullReferenceException異常
           Console.WriteLine(s4?.ToString());
           Console.WriteLine(s4.ToString()); //  拋出異常 System.NullReferenceException

11 類型 switch

       static void CheckType(object obj)
       {
           switch (obj)
           {
               case int i :
                   Console.WriteLine($"int {i}");
                   break;
               case float i :
                   Console.WriteLine($"float {i}");
                   break;
               case double i :
                   Console.WriteLine($"double {i}");
                   break;
               case string i :
                   Console.WriteLine($"string {i}");
                   break;
               default:
                   Console.WriteLine($"unknow {obj}");
                   break;
           }
       }

       public static void Test11()
       {
           CheckType(1); // int 1
           CheckType(1F); // float 1
           CheckType(11.1); // double 11.1
           CheckType("Hi Afra"); // string Hi Afra
           CheckType(false); // unknow False
       }

12 foreach

foreach語句遍歷可枚舉對象的每一個元素。

           // 遍歷 Afra55 的每一個元素,最終輸出 A f r a 5 5 
           foreach (var VARIABLE in "Afra55")
           {
               Console.Write(VARIABLE + " ");
           }

四 創建類型

1 字段

修飾符:
·靜態修飾符:static
·訪問權限修飾符:public internal private protected
·繼承修飾符:new
·不安全代碼修飾符:unsafe
·只讀修飾符:readonly
·線程訪問修飾符:volatile

2 方法

修飾符:
·靜態修飾符:static
·訪問權限修飾符:public internal private protected
·繼承修飾符:new virtual abstract override sealed
·部分方法修飾符:partial
·非託管代碼修飾符:unsafe extern
·異步代碼修飾符:async

解構器

一個解構器(或稱之爲解構方法)就像構造器的反過程:構造器使用若干值作爲參數,並且將它們賦值給字段;而解構器則相反將字段反向賦值給若干變量。解構方法的名字必須爲Deconstruct,並且擁有一個或多個out參數.

        class Rectangle
        {
            public readonly float Width, Height;

            public Rectangle(float width, float height)
            {
                Width = width;
                Height = height;
            }

            public void Deconstruct(out float width, out float height)
            {
                width = Width;
                height = Height;
            }
        }

        public static void Test1()
        {
            var rect = new Rectangle(3, 4);
            // 調用解構器
            (float width, float height) = rect;
            Console.WriteLine(width + " " + height); // 3 4
        }

3 屬性

屬性(Property)和字段很類似,但是屬性內部像方法一樣含有邏輯,屬性比字段多了 get 或 set 代碼塊。

        private int field;
        public int Property
        {
            get { return field; }
            set { field = value; }
        }

屬性支持以下的修飾符:
·靜態修飾符:static
·訪問權限修飾符:public internal private protected
·繼承修飾符:new virtual abstract override sealed
·非託管代碼修飾符:unsafe extern

上面的例子可以寫成自動屬性省略 field:

   public int Property { get; set; }

編譯器會自動生成一個後臺私有字段,該字段的名稱由編譯器生成且無法引用。

4 常量

const 關鍵字聲明常量,常量必須用值初始化。常量是一種值永遠不會改變的靜態字段。常量會在編譯時靜態賦值,編譯器會在常量使用點上直接替換該值。

        public const string Message = "Hi Vicrtoria";

5 終結器

在垃圾回收器回收未引用的對象佔用的內存前調用。

       class ClassFinalizerTest
        {
            // 終結器, 是 C# 語言重寫 Object 的 Finalize 方法的語句。
            ~ClassFinalizerTest()
            {
                
            }
        }

6 分部類型和方法

主要用於擴展類型和改寫方法。
類型可以分開定義,比如在多個文件中定義。
每個部分的聲明必須包含 partial:

7 object 類型

object 類型是所有類型的最終基類,任何類型都能向上轉換爲objhect類型。

8 裝箱和拆箱

裝箱是將值類型實例轉換爲引用類型實例的行爲。

int x = 9;
object obj = x;

拆箱需要顯式類型轉換。

int y = (int)obj;

9 結構體

  • 結構體是值類型,類是引用類型;
  • 結構體不支持繼承;
  • 結構體可以包含:無參構造器,字段初始化器,終結器,虛成員或protected成員;
  • 結構體隱式包含一個無法重寫的無參數構造器,將字段按位置爲0;
  • 定義結構體的構造器時,必須顯式爲每一個字段賦值。
        public struct MyStruct
        {
            public int x;
            public int y;

            public MyStruct(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }
        
        public static void Test5()
        {
            MyStruct myStruct =new MyStruct(1, 2);
            Console.WriteLine(myStruct.x + " " + myStruct.y); // 1 2
            MyStruct myStruct2 = new MyStruct();
            Console.WriteLine(myStruct2.x + " " + myStruct2.y); // 0 0 

        }

10 訪問權限修飾符

  • public:完全訪問權限。枚舉類型成員或接口成員隱含的可訪問性。
  • internal:僅可以在程序集內訪問,或供友元程序集訪問。這是非嵌套類型的默認可訪問性。
  • private:僅可以在包含類型中訪問。這是類或者結構體成員的默認可訪問性。
  • protected:僅可以在包含類型或子類中訪問。
  • protected internal:protected和internal可訪問性的並集。

11 接口

  • 接口只爲成員提供定義而不提供實現;
  • 接口的成員都是隱式抽象的。類可以包含抽象的成員和有具體實現的成員;
  • 一個類(或者結構體)可以實現多個接口。而一個類只能夠繼承一個類,結構體則完全不支持繼承(只能從System.ValueType派生);
  • 接口可以從其他接口派生;
        public interface IInterface
        {
            bool MoveNext();
            object Current { get; }
            void Reset();

            void Name();
        }
        
        public interface IInterface2 : IInterface
        {
            // 從  IInterface 派生
        }
        
        public interface IName
        {
            string Name();
        }
        
        public class MyImp:IInterface, IName
        {
            private int count = 11;
            public bool MoveNext()
            {
                return count-- > 0;
            }

            public object Current { get => count; }
            public void Reset()
            {
                throw new NotImplementedException();
            }

            void IInterface.Name()
            {
                Console.WriteLine("IInterface.Name()");
            }

            string IName.Name()
            {
                Console.WriteLine("IName.Name()");
                return "IName";
            }
        }
        public static void Test6()
        {
            IInterface myImp = new MyImp();
            while (myImp.MoveNext())
            {
                Console.Write($"{myImp.Current} "); // 10 9 8 7 6 5 4 3 2 1 0 
            }

        }

12 枚舉

  • 每一個枚舉成員都對應一個整數;
  • 按照枚舉成員的聲明順序,自動按照0、1、2……進行常量賦值;
        public enum  MyEnum
        {
            One, Two, Three
        }
        
        public enum MyByteEnum:long
        {
            // 可以指定數字類型,也可以顯式的指定數字
            One = 1, Tow, Three = 90, Four
        }
        public static void Test7()
        {
            MyEnum one = MyEnum.One;
            MyEnum Two = MyEnum.Two;
            MyEnum Three = MyEnum.Three;
            
            Console.WriteLine((long)MyByteEnum.Four); // 91
            Console.WriteLine(MyByteEnum.Four.ToString()); // Four
            Console.WriteLine(sizeof(MyByteEnum)); // 8
        }

13 泛型

泛型是爲了代碼能夠跨類型複用而設計的。

        public class Stack<T>
        
        {
            private int position;
            private T[] data = new T[100];
            public void Push(T obj) => data[position++] = obj;
            public T Pop() => data[--position];

            void Zap<T>(T[] array)
            {
                for (int i = 0; i < array.Length; i++)
                {
                    // default 關鍵字可以用於獲取泛型類型參數的默認值,引用類型默認值是 null,值默認值是0
                    array[i] = default(T);
                }
            }
        }
        
        public class MStack<T, T1, T2>
        {
            void Init<T>(T[] array) where T:new() // where 約束泛型
            {
                
            }
            
        }
        
        public static void Test8()
        {
            
            var stack = new Stack<int>();
            stack.Push(1);
            var stack2 = new Stack<String>();
            stack2.Push("1");
            var stack3 = new Stack<double>();
            stack3.Push(1.0);

            Type a = typeof(Stack<>);
        }

有協變和逆變的概念,我瞌睡了。
out 協變子轉父,in 逆變父轉子。你在說啥子~

        public interface IComparerOut<out T>
        {
        }
        public interface IComparerIn<in T>
        {
            
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章