)《Visual C# 最佳實務》第一章 程式設計 (二):資料類型

來源:互聯網
上載者:User

http://www.cnblogs.com/open24hours/archive/2010/04/20/1715990.html

第一章 程式設計

  “要想成為真正的程式員,我們需要進行一場洗禮。”
  “程式 = 資料結構 + 演算法。”這樣的公式很精闢,它越過了表層直接描述了程式的本質。不僅如此,這樣幾個簡單的單詞也讓我們明白“我們應該學習什麼內容?”。人們做任何事都有一定的程式,只是沒有意識到。有些人將日常行動列成表格,這就是“編寫程式”。

本章的學習重點:

◆    基礎資料型別 (Elementary Data Type)
◆    實值型別與參考型別
◆    var類型

1.2資料類型

  在本文中,我們首先從介紹基礎資料型別 (Elementary Data Type)開始,然後迅速進入關於參考型別和實值型別的討論。對所有的開發人員來說,熟練掌握參考型別和數實值型別的應用差別尤其重要。在編寫代碼的過程中,如果對這兩種類型使用不當會導致程式Bug並引起效能問題。

1.2.1基礎資料型別 (Elementary Data Type)

  到目前為止,只用過少量基礎資料型別 (Elementary Data Type),而且書中只對它們一筆帶過,沒有詳細解釋。C#中存在著大量類型,而且可以合并不同的類型來建立新類型。然而,C#中有幾種類型非常簡單,它們被視為其他所有類型的基礎。這些類型稱為預定義類型(predefined type)或者基本類型(primitive type)。C#語言的基本類型包括8種整數類型、2種浮點類型、1種高精度類型、1種布爾類型、1種字元類型以及1種字串類型。下面我們將探討這些基礎資料型別 (Elementary Data Type)。
  1、    整數類型
  C#語言共有8種整數類型,我們可以選擇最恰當的一種資料類型來存放資料,避免浪費資源。

類型
大小
範圍
BCL名稱

sbyte
8位
-128~127
System.SByte

byte
8位
0~255
System.Byte

short
16位
-32768~32767
System.Int16

ushort
16位
0~65535
System.UInt16

int
32位
-2147483648~2147483 647
System.Int32

uint
32位
0~4294967295
System.Int32

long
64位
-9223372036854775808~9223372036854775807
System.Int64

ulong
64位
0~18 446 744 073 709 551 615
System.Int64

  C#的所有基本類型都有一個簡短名稱和一個完整名稱。完整名稱對應於BCL中命名的類型。這個名稱在所有語言中都是相同的,而且它對程式集中的類型進行了唯一性的標識。因為基礎資料型別 (Elementary Data Type)是其他類型的基礎,所以C#為基礎資料型別 (Elementary Data Type)的完整名稱提供了簡短名稱或者縮寫。其實從編譯器的角度看,兩種名稱是完全一樣的,最終都將產生同樣的代碼。事實上,假如檢查一下最終產生的CIL代碼,會發現其中沒有任何跡象顯示原始碼中使用的是哪一種名稱。下面我們來看一個範例:

1 using System;
2 namespace Microsoft.Example
3 {
4     public class TestInt
5      {
6         static void Main(string[] args)
7         {
8              byte byteValue = 2;
9              short shortValue = -4;
10             int intValue = -8;
11             long longValue = -10;
12             Console.WriteLine("byte類型的變數值為:" + byteValue);
13             Console.WriteLine("short類型的變數值為:" + shortValue);
14             Console.WriteLine("int類型的變數值為:" + intValue);
15             Console.WriteLine("long類型的變數值為:" + longValue);
16         }
17     }
18 }
  上述代碼中,我們定義了一系列的整數類型,它們之間的差異主要是:不同類型的容量不同,還有就是數值的正負符合不同。第7行我們定義了一個byte類型,這個類型只能儲存正整數。很多時候因為int既可以儲存正數,也可以儲存負數,uint才只能儲存正整數。所有,我們按著這個思維定勢,很容易搞混淆,以為byte可以儲存正整數,也可以儲存負整數。其實不是這樣的,根本就沒有ubyte這個類型,只有sbyte,這個大家一定要記住了。
  最後的輸出結果是:
  byte類型的變數值為:2
   short類型的變數值為:-4
  int類型的變數值為:-8
  long類型的變數值為:-10
  在真實的項目中,我們定義變數類型的時候,通常不採用intValue方式來命名變數,這種方式叫做匈牙利命名法,就是把變數類型跟放在變數的前面,這種方法的好處是對變數的類型一目瞭然,我們這裡這麼寫,也是因為這個原因,讓讀者比較快的分別不同的變數。在項目中,因為開發工具(IDE)的類型提示功能已經很強大了,根本不需要這麼做。
  2、    浮點類型
  浮點數的精度是可變的。如果讀取本來是0.1的一個浮點數,那麼可能很容易讀取成0.099 999 999 999 999 999或者0.100 000 000 000 000 000 1或者其他非常接近0.1的一個數。因為原始數字實在是太大了。根據定義,一個浮點數的精度與它包含的數字個數成正比。準確地說,精度取決於有效數位個數,而不是一個固定值,比如±0.01。
  類型    大小    範圍                        BCL名稱            有效數字
  Float    32位      ±1.5×1045~±3.4×1038         System.Single            7
  Double     64位      ±5.0×10324~±1.7×10308     System.Double        15~16
  3、    decimal類型
  C#有一個數實值型別具有128位精度。它適合大而精確的計算,尤其是金融計算。
  類型    大小    範圍                        BCL名稱            有效數字
  Decimal   128位     ±1.0×1028~±7.9×1028         System.Decimal        28~29
  與浮點數不同,decimal類型保證範圍內的所有十進位數都是精確的。所以,對於decimal類型來說,0.1就是0.1,而不是一個近似值。不過,雖然decimal類型具有比浮點類型更高的精度,但它的範圍較小。所以,從浮點類型轉換為decimal類型可能發生溢出錯誤。此外,decimal的計算速度要稍微慢一些。
  除非超過範圍,否則decimal數字表示的十進位數都是完全準確的。與此相反,用浮點數來表示十進位數,則可能造成舍入錯誤。decimal類型和C#的浮點類型之所以存在這個區別,是因為decimal類型的指數是十進位數,而浮點類型的指數是二進位的。
  預設情況下,如果輸入一個帶小數點的字面值,編譯器會自動把它解釋成double類型。相反,一個整數值(沒有小數點)通常預設為int,前提是該值不是太大,以至於無法用int來儲存。如果值太大,編譯器會把它解釋成long。此外,C#編譯器允許向一個非int的數實值型別賦值,前提是字面值對於目標資料類型來說是合法的。例如,short s = 42和byte b = 77都是允許的。下面我們來看一個範例: 
1 namespace Microsoft.Example
2 {
3     public class TestDoubleAndDecimal
4     {
5         static void Main(string[] args)
6         {
7             double doubleValue = 1.618033988749895;                //指定一個double字面量
8             decimal decimalValue = 1.618033988749895m;            //指定一個decimal字面量
9             Console.WriteLine("double類型的變數值為" + doubleValue);        //輸出doubleValue
10             Console.WriteLine("decimal類型的變數值為" + decimalValue);    //輸出結decimalValue
11         }
12     }
13 }
  上述代碼中,我們在第7行定義了一個double類型的資料doubleValue,它的值是1.618033988749895,輸出結果是 1.61803398874989。前面講過,C#有許多不同的數實值型別。一個字面值被直接放到C#代碼中。由於帶小數點的數預設為double資料類型,輸出是1.61803398874989(最後一個數字5丟失了),這符合我們預期的double值的精度。
  第8行,我們定義了一個decimal類型的資料,要查看具有完整精度的數字,必須將字面值顯式地聲明為decimal類型,這是通過追加一個m(或者M)來實現的。輸出結果是1.618033988749895。
  現在,代碼的輸出與預期的結果相同:1.618033988749895。注意,d表示double,之所以用m表示decimal,是因為這種資料類型經常用在貨幣(monetary)計算中。還可以使用f和d作為尾碼,將一個字面量顯式地聲明為float或者double。
  最後的輸出結果是:
  double類型的變數值為1.61803398874989
  decimal類型的變數值為1.618033988749895.
  4、    布爾類型
  另一個C#基本類型是布爾(Boolean)類型bool、也可以稱為條件類型。在條件陳述式和運算式中,它用於表示真或假。允許的值包括關鍵字true 和false。bool的BCL名稱是System.Boolean。一個布爾類型的字面值使用關鍵字true和false。例如,為了在不區分大小寫前提下比較兩個字串,可以調用 string.Compare()方法,並傳遞bool字面量true。
  例如:以不區分大小寫方式比較兩個字串
  string option = “/help”;
  int comparison = string.Compare(option, "/Help", true);
  在這個例子中,我們以不區分大小寫方式比較變數option的內容和字面量/Help,並將結果賦給comparison。雖然從理論上說,一個位元就足以容納一個布爾類型的值,但bool資料類型的實際大小是一個位元組。下面我們來看一個範例:
1 namespace Microsoft.Example
2 {
3     public class TestBool
4     {
5         static void Main(string[] args)
6         {
7             bool boolFlag = false;                //定義一個bool類型的資料
8             Console.WriteLine("bool類型的變數值為" + boolFlag.ToString());        //輸出結果
9         }
10     }
11 }
  上述代碼中,第7行我們定義了一個bool類型的資料,並賦予false值。bool類型只有兩個值,一個是true值,一個是false值。然後,我們在第8行代碼中,把結果輸出來。
  最後的輸出結果是:
  bool類型的變數值為False
  5、    字元類型
  字元類型char用於表示16位字元,其取值範圍對應於Unicode字元集。從技術上說,一個char的大小等同於一個16位不帶正負號的整數(ushort)的大小,後者的取值範圍是0~65 535。然而,char是C#中的一個獨一無二的類型,不應該像這樣來對待。char的BCL名稱是System.Char。
  Unicode 是用於對人類大多數語言中的字元進行表示的一個國際性標準。它便於電腦系統構建本地化的應用程式,為不同的語言文化顯示具有本地特色的字元。令人遺憾的是,並不是所有Unicode字元都可以用一個16位char來表示。剛開始提出Unicode的概念的時候,它的設計者以為16位已經足夠。但隨著支援的語言越來越多,才發現當初的假定是錯誤的。結果是,一些Unicode字元要由一對稱為“代理項”的char構成,總共32位。
  為了輸入一個字元類型的字面量,需要將字元放到一對單引號中,比如'A'。所有鍵盤字元都可以像這樣輸入,包括字母、數字以及特殊符號。有的字元不能直接插入原始碼,需要進行特殊處理。這些字元有一個反斜線(\)首碼,並跟隨一個特殊字元代碼。我們將反斜線和特殊字元代碼統稱為逸出序列(escape sequence)。例如,'\n'代表分行符號,而'\t'代表定位字元。由於反斜線標誌著一個逸出序列的開始,所以不能再用來直接表示一個反斜線字元,而要使用'\\'來表示一個反斜線字元。
下面我們來看一個範例:
1 namespace Microsoft.Example
2 {
3     public class TestChar
4     {
5         static void Main(string[] args)
6         {
7            char charValue = '\'';        //定義了一個char類型的變數
8            System.Console.WriteLine("輸出結果是:" + charValue);        //輸出結果
9         }
10     }
11 }
  上述代碼中,第7行我們定義了一個char類型的變數charValue,然後對它進行賦值’\’’。字元類型是使用單引號’來容納字元的。然後我們使用WriteLine向控制台輸出結果。
  輸出結果是:’
  6、    字串類型
  C#的基底字元串類型是string,它的BCL名稱是System.String。對於已經熟悉了其他語言的開發人員,string的一些特點或許是他們意想不到的。比如字串逐字前置詞字元@,以及string屬於不可變類型的事實。
  String可以將字面量字串輸入代碼,具體做法是將文本放入雙引號(")內,就像HelloWorld程式中那樣。
  在C#中,可以在一個字串前面使用@符號,指明逸出序列不被處理。這樣產生結果是一個逐字字串字面量(verbatim string literal),它不僅將反斜線當作一般字元來處理,而且還會逐字解釋所有空白字元。例如:使用逐字字串字面量來顯示一個三角形
1 using System;
2 class TestString
3 {
4   static void Main()
5   {
6      System.Console.Write( @ "begin        //注意,這裡使用了@符號
7 \n                                        //注意,這裡使用了分行符號“\n”,但並沒有轉義
8 end");
9   }
10 }
  上述代碼中,第6行我們使用了@符號來指明忽略逸出序列,按字面上顯示來逐字輸出。在實際項目中,這個功能很好使,我們經常會用它來處理路徑字串,這樣既簡單,又清晰。第7行代碼中我們先進行了斷行符號換行,然後才輸入”\n”的。這裡需要特別注意,輸出結果中的斷行符號換行並不是“\n”產生了效果,而是我們在編寫代碼的時候進行了斷行符號換行,所有使用了@符號後,也會進行斷行符號換行,“\n”是完全被忽略的。
  最後的輸出結果是:
  begin
  \n
  End

1.2.2實值型別與參考型別

  首先,變數是儲存資訊的基本單元,而對於電腦內部來說,變數就相當於一塊記憶體空間。C#語言中的變數可以劃分為實值型別和參考型別兩種:
  實值型別:基礎資料型別 (Elementary Data Type)、結構類型、枚舉類型等
  參考型別:類、數組、介面等。
  (一)實值型別和參考型別記憶體配置
  實值型別是在棧中操作,而參考型別則在堆中分配儲存單元。棧在編譯的時候就分配好記憶體空間,在代碼中有棧的明確定義,而堆是程式運行中動態分配的記憶體空間,可以根據程式的運行情況動態地分配記憶體的大小。因此,實值型別總是在記憶體中佔用一個預定義的位元組數(比如,int佔用4個位元組,即32位)。當聲明一個實值型別變數時,運行時會在堆棧中分配記憶體空間,並儲存這個變數所包含的值。.NET會自動維護一個棧指標,它包含棧中下一個可用記憶體空間的地址。棧是先入後出的,棧中最上面的變數總是比下面的變數先離開範圍。當一個變數離開範圍時,棧指標向下移動被釋放變數所佔用的位元組數,仍指向下一個可用地址。注意,實值型別的變數在使用時必須初始化。
  而參考型別的變數則在堆中分配一個記憶體空間,這個記憶體空間包含的是對另一個記憶體位置的引用,這個位置是託管堆中的一個地址,即存放此變數實際值的地方。.NET也自動維護一個堆積指標,它包含堆中下一個可用記憶體空間的地址,但堆不是先入後出的,而是在對象不使用的時候才釋放記憶體,.NET將定期執行垃圾收集。垃圾收集器遞迴地檢查應用程式中所有的對象引用,當發現引用不再有效對象使用的記憶體無法從程式中訪問時,該記憶體就可以回收(除了fixed關鍵字固定在記憶體中的對象外)。
  但實值型別在棧上分配記憶體,而參考型別在託管堆上分配記憶體,卻只是一種籠統的說法。更詳細準確地描述是:
  1、對於實值型別的執行個體,如果做為方法中的局部變數,則被建立線上程棧上;如果該執行個體做為類型的成員,則作為類型成員的一部分,連同其他類型欄位存放在託管堆上,
  2、參考型別的執行個體建立在託管堆上,如果其位元組小於85000byte,則直接建立在託管堆上,否則建立在LOH大對象堆上。
  例如:
  public class Test
      {
          private int i;        //作為Test執行個體的一部分,與Test的執行個體一起被建立在託管堆上
          public Test()
          {
              int j = 0;        //作為局部實量,j的執行個體被建立在執行這段代碼的線程棧上
          }
      }
  (二)嵌套結構的記憶體配置
  所謂嵌套結構,就是參考型別中嵌套有實值型別,或實值型別中嵌套有參考型別。
  參考型別嵌套實值型別是最常見的,上面的例子就是典型的例子,此時實值型別是內聯在參考型別中
  實值型別嵌套參考型別時,該參考型別作為實值型別成員的變數,將在堆棧上保留該參考型別的引用,但參考型別還是要在堆中分配記憶體的。
  (三)關於數組記憶體的分配
  考慮當數群組成員是實值型別和參考型別時的情形:
  成員是實值型別:比如int[] arr = new int[5]。arr將儲存一個指向託管堆中4*5byte(int佔用4位元組)的地址的引用,同時將所有元素賦值為0;
  參考型別:myClass[] arr = new myClass[5]。arr線上程的堆棧中建立一個指向託管堆的引用。所有元素被置為null。
  (四)實值型別和參考型別在傳遞參數時的影響
  由於實值型別直接將它們的資料存放在棧中,當一個實值型別的參數傳遞給一個方法時,該值的一個新的拷貝被建立並被傳遞,對參數所做的任何修改都不會導致傳遞給方法的變數被修改。而參考型別它只是包含引用,不包含實際的值,所以當方法體內參數所做的任何修改都將影響傳遞給方法調用的參考型別的變數。下面我們來看一個樣本:
1 using System;
2 namespace Microsoft.Example
3 {
4     public class TestPassParam
5     {
6         static void Main()
7         {
8             int i = 0;                //定義一個實值型別(整數)的變數
9             int[] intArr = { 0 };        //定義一個參考型別(數組)的變數
10            SetValues(i, intArr);    //進行參數傳遞
11            Console.WriteLine("i={0},intArr[0]={1}", i, intArr[0]);        //輸出結果
12         }
13         public static void SetValues(int i, int[] intArr)
14         {
15             i = 10;                //改變實值型別的值
16             intArr[0] = 10;            //改變參考型別的值
17         }
18     }
19 }
  上述代碼中,第8行我們定義了一個實值型別的變數i,它是整數類型的資料。第9行我們定義了一個參考型別的變數intArr,它是一個數組,數組的相關知識我們會在後面詳細講到,這裡我們只需要知道它是一個參考型別就可以了。然後我們調用SetValues方法把實值型別和參考型別的參數傳遞過去進行改變。
  最後,我們在第11行輸出結果發現,實值型別的i在SetValues方法裡面對其值的改變是無效的,依然為0,但是參考型別intArr在SetValues方法裡面對其值的改變卻依然保留下來了,值為10。
  最後的輸出結果是:
  i=0,intArr[0]=10
  (五)裝箱和拆箱
  裝箱是將一個實值型別轉換為一個物件類型(object),而拆箱則是將一個物件類型顯式轉換為一個實值型別。對於裝箱而言,它是將被裝箱的實值型別複製一個副本來轉換,而對於拆箱而言,需要注意類型的相容性,比如,不能將一個值為“a”的object類型轉換為int的類型。
下面我們來看一個樣本:
1 using System;
2 namespace Microsoft.Example
3 {
4     public class TestBox
5     {
6         static void Main()
7         {
8             int i = 10;                            //定義一個整數變數
9             object o = i;                            //進行裝箱操作
10             if (o is int)                            //判斷是否裝箱
11             {
12                 Console.WriteLine("i已經被裝箱");
13             }
14             int j = (int)o;                        //進行拆箱操作
15             Console.WriteLine("j已經被拆箱");
16             Console.WriteLine("i的值為" + i);
17             Console.WriteLine("j的值為" + j);
18         }
19     }
20 }
  上述代碼中,第8行中我們定義了一個整形變數i,然後我們在第9行中進行了裝箱操作,如果裝箱成功,那麼o的引用就是int類型。我們可以通過 o.GetType()可以得知o是int類型。第14行,我們通過強制類型轉換,對o變數進行拆箱,得到一個整數類型的資料,我們把這個資料賦值給j整型變數進行儲存。
  最後的輸出結果是:
  i已經被裝箱
  j已經被拆箱
  i的值為10
  j的值為10
  (六)關於string
  string是參考型別,但卻與其他參考型別有著一點差別。可以從以下兩個方面來看:
  1、    String類繼承自object類。而不是System.ValueType。
  2、string本質上是一個char[],而Array是參考型別,同樣是在託管的堆中分配記憶體。但String作為參數傳遞時,卻有實值型別的特點,當傳一個string類型的變數進入方法內部進行處理後,當離開方法後此變數的值並不會改變。原因是每次修改string的值時,都會建立一個新的對象。

1.2.3 var類型

  在C#3.0中增加了一個var關鍵字,這個關鍵字和JavaScript的var類似,我們都可以用var來聲明任意類型的局部變數。但又有不同,在C#語言中它僅僅是一個關鍵字,不代表一種新的類型,它僅是負責告訴編譯器,該變數需要根據初始設定式來推斷變數的類型,而且只能是局部變數。 var 聲明變數之後,變數類型就確定下來了,不會再變,這和JavaScript有本質區別。
   var關鍵字可以這樣聲明任意類型的局部變數:
  var name = "csharp";
  var names = new string[] { “C#”, ”VB”, ”C++” };
  等價於下面語句:
  string name = "csharp";
  string[] names = new string[] { “C#”, ”VB”, ”C++” };
  注意,在聲明時必須同時給var類型賦值,因為聲明的具體類型依賴於賦值號右邊的運算式類型。運算式的類型也不可以是空(null)類型,編譯器無法根據null來推斷出局部變數的類型。
  下面向大家介紹兩個與var關鍵字密切相關的概念:隱式類型的局部變數、匿名型別。
  1、隱式類型的局部變數
  我們在上面介紹var關鍵字的概念的時候就知道,可以把值賦予局部變數var類型,它是通過右邊的運算式的類型推斷出來的隱式類型,而不是顯式類型。隱式類型可以是內建類型、使用者定義型別或 .NET Framework 類庫中定義的類型。
  下面的樣本示範了使用var關鍵字聲明隱式類型的局部變數:
  var name = "csharp";
  var names = new string[] { “C#”, ”VB”, ”C++” };
  我們開始介紹概念的時候用的就是隱式類型的局部變數,這裡我們用相同是樣本,這樣大家就不會感到混亂。需要瞭解的一點是,var關鍵字並不意味著“變體”,也不表示該變數是鬆散類型化變數或後期綁定變數。它只是表示由編譯器確定和分配最適當的類型。
  var 關鍵字可在下面的上下文中使用:
  在 for 初始化語句中:for(var x = 1; x < 10; x++)
  在 foreach 初始化語句中:foreach(var item in list){...}
  在 using 語句中:using (var file = new StreamReader("C:\\myfile.txt")) {...}
  有關for,foreach,using更多資訊,請參見“基本結構”章節。在很多情況下,var是可選的,它只是提供了文法上的便利。
2、匿名型別
  匿名型別提供了一種方便的方法,可以將一組唯讀屬性封裝到一個對象中,而無需首先顯式定義這個對象的類型。類型名由編譯器產生,並且不能在原始碼級使用。這些屬性的類型由編譯器推斷。下面的樣本示範兩個分別名為Amount和Message的屬性來初始化的匿名型別。
  var v = new { Amount = 100, Message = "Hello" };
  這裡的v就是一個匿名型別,注意v這裡是一個變數名,並不是類的名字。前面還有一個var,這又是什麼呢?這就是隱式類型局部變數。匿名型別通常用在查詢運算式的select子句中,以便返回源序列中每個對象的屬性子集。有關查詢的更多資訊,請參見LINQ查詢運算式。
  匿名型別是使用new運算子和一個或多個公用唯讀屬性群組成的類類型。不允許包含其他種類的類成員(如方法或事件)。匿名型別不能強制轉換為除object以外的任何介面或類型。
  最常見的方案是用其他類型的一些屬性初始化匿名型別。在下面的樣本中,假定一個名為Product的類包含Color和Price屬性以及其他幾個您不感興趣的屬性。Products是一個Product對象集合。匿名型別聲明以new關鍵字開始。它初始化了一個只使用Product的兩個屬性的新類型。這將導致在查詢中返回較少數量的資料。
  var productQuery =
    from prod in products
    select new { prod.Color, prod.Price };
  foreach (var v in productQuery)
  {
   Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
  }
  在將匿名型別分配給變數時,必須使用var構造初始化該變數。這是因為只有編譯器能夠訪問匿名型別的基礎名稱,不能在原始碼級使用這個名稱。
  最後,跟大家說一下,過多使用var可能使原始碼的可讀性變差。建議僅在必要時使用var,即僅在該變數將用於儲存匿名型別或匿名型別集合時才使用它。我想var的出現其實完全是為了配合匿名型別而出現的。在linq中應用也比較多,也就是說對象是匿名型別,或者對象是難以預測的類型的時候。像這樣的代碼var age = 10;還是少用為好,這樣型別安全,代碼的可閱讀性也高。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.