<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
如果需要使用同一型別的多個物件,可以使用陣列和集合(後面介紹)。C#用特殊的記號宣告,初始化和使用陣列。Array類在後臺發揮作用,它為陣列中的元素排序和過濾提供了多個方法。使用列舉元,可以迭代陣列中的所有元素。
如果需要使用不同型別的多個物件,可以使用Tuple(元組)型別。
陣列是一種資料結構,它可以包含同一個型別的多個元素。
在宣告陣列時,先定義陣列中的元素型別,其後是一對空方括號和一個變數名。
int[] myArray;
宣告了陣列之後,就必須為陣列分配記憶體,以儲存陣列的所有元素。陣列是參照型別,所以必須給它分配堆上的記憶體。為此,應使用new運運算元,指定陣列中元素的型別和數量來初始化陣列的變數。
myArray = new int[4];
在宣告和初始化陣列後,變數myArray就參照了4個整數值,它們位於託管堆上:
在指定了陣列的大小後,就不能重新設定陣列的大小。如果事先不知道陣列中應包含多少個元素,就可以使用集合。
除了在兩個語句中宣告和初始化陣列之外,還可以在一個語句中宣告和初始化陣列:
int[] myArray = new int[4];
還可以使用陣列初始化器為陣列的每個元素複製。陣列初始化器只能在宣告陣列變數時使用,不能在宣告陣列之後使用。
int[] myArray = new int[4]{1,3,5,7};
如果用花括號初始化陣列,可以不指定陣列的大小,因為編譯器會自動統計元素的個數:
int[] myArray = new int[]{1,3,5,7};
也可以使用更簡單的形式:
int[] myArray = {1,3,5,7};
在宣告和初始化陣列之後,就可以使用索引器存取其中的元素了。陣列只支援有整型引數的索引器。
索引器總是以0開頭,表示第一個元素。可以傳遞給索引器的最大值是元素個數減1,因為索引從0開始:
int[] myArray = {1,3,5,7}; int v1 = myArray[0]; int v2 = myArray[1]; myArray[3] = 4;
可以使用陣列的Length屬性獲取元素的個數。
陣列除了能宣告預定義型別的陣列,還可以宣告自定義型別的陣列。
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public override string ToString() { return String.Format("{0} {1}", FirstName, LastName); } } Person[] myPersons = new Person[2]; myPersons[0] = new Person { FirstName = "Ayrton", LastName = "Senna" }; myPersons[1] = new Person { FirstName = "Michael", LastName = "Schumacher" };
如果陣列中的元素是參照型別,就必須為每個陣列元素分配記憶體。如果使用了陣列中未分配記憶體的元素,就會丟擲NullReferenceException型別的異常。
下面是記憶體情況:
對自定義型別也可以使用陣列初始化器:
Person[] myPersons2 = { new Person { FirstName="Ayrton", LastName="Senna"}, new Person { FirstName="Michael", LastName="Schumacher"} };
多維陣列用兩個或多個整數來索引。
在C#中宣告多維陣列,需要在方括號中加上逗號。陣列在初始化時應指定每一維的大小(也稱為階)。
int[,] twoDim = new int[3,3]; twoDim[0,0] = 1; twoDim[0,1] = 2; twoDim[0,2] = 3; twoDim[1,0] = 4; twoDim[1,1] = 5; twoDim[1,2] = 6; twoDim[2,0] = 7; twoDim[2,1] = 8; twoDim[2,2] = 9;
宣告陣列之後,就不能修改其階數了。
也可以使用初始化器來初始化多維陣列:
int[,] twoDim ={ {1,2,3}, {4,5,6}, {7,8,9} };
使用陣列初始化器時,必須初始化陣列的每個元素,不能遺漏任何元素。
宣告一個三位陣列:
int[,,] threeDim ={ {{1,2},{3,4}}, {{5,6},{7,8}}, {{9,10},{11,12}} }; Console.WriteLine(threeDim[0,1,1]);
二維陣列的大小對應於一個矩形,而鋸齒陣列的大小設定比較靈活,在鋸齒陣列中,每一行都可以有不同的大小。
在宣告鋸齒陣列時,要依次放置左右括號。在初始化鋸齒陣列時,只在第一對方括號中設定該陣列包含的行數。定義各行中元素個數的第二個方括號設定為空,因為這類陣列的每一行包含不同的元素個數。之後,為每一行指定行中的元素個數:
int[][] jagged = new int[3][]; jagged[0] = new int[2]{1,2}; jagged[1] = new int[4]{3,4,5,6}; jagged[2] = new int[3]{7,8};
迭代鋸齒陣列中的所有元素的程式碼可以放在巢狀的for迴圈中。在外層的for迴圈中迭代每一行,在內層的for迴圈中迭代一行中的每個元素:
for(int row = 0;row<jagged.Length;row++) { for(int element = 0;element<jagged[row].Length;element++) { Console.WriteLine("row:{0}, element:{1},value:{2}",row,element,jagged[row][element]); } }
用方括號宣告陣列是C#中使用Array類的表示法。在後臺使用C#語法,會建立一個派生自抽象基礎類別Array的新類。這樣,就可以使用Array類為每個C#陣列定義的方法和屬性了。
Array類實現的其它屬性有LongLength和Rank。如果陣列包含的元素個數超出了整數的取值範圍,就可以使用LongLength屬性來獲得元素個數。使用Rank屬性可以獲得陣列的維數。
Array類是一個抽象類,所以不能使用建構函式來建立陣列。但除了使用C#語法建立陣列範例之外,還可以使用靜態方法CreateInstance()建立陣列。如果事先不知道元素的型別,該靜態方法就很有用,因為型別可以作為Type物件傳遞給CreateInstance()方法。
CreateInstance()方法的第一個引數是元素的型別,第二個引數定義陣列的大小。
可以使用SetValue()方法設定對應元素的值,用GetValue()方法讀取對應元素的值。
Array intArray1 = Array.CreateInstance(typeof(int), 5); for (int i = 0; i < 5; i++) { intArray1.SetValue(33, i); } for (int i = 0; i < 5; i++) { Console.WriteLine(intArray1.GetValue(i)); }
還可以將已經建立的陣列強制轉換稱宣告為int[]的陣列:
int[] intArray2 = (int[])intArray1;
CreateInstance()方法有許多過載版本,可以建立多維陣列和索引不基於0的陣列。
//建立一個2X3的二維陣列,第一維基於1,第二維基於10: int[] lengths = { 2, 3 }; int[] lowerBounds = { 1, 10 }; Array racers = Array.CreateInstance(typeof(Person), lengths, lowerBounds); racers.SetValue(new Person { FirstName = "Alain", LastName = "Prost" }, index1: 1, index2: 10); racers.SetValue(new Person { FirstName = "Emerson", LastName = "Fittipaldi" }, 1, 11); racers.SetValue(new Person { FirstName = "Ayrton", LastName = "Senna" }, 1, 12); racers.SetValue(new Person { FirstName = "Michael", LastName = "Schumacher" }, 2, 10); racers.SetValue(new Person { FirstName = "Fernando", LastName = "Alonso" }, 2, 11); racers.SetValue(new Person { FirstName = "Jenson", LastName = "Button" }, 2, 12); Person[,] racers2 = (Person[,])racers; Person first = racers2[1, 10]; Person last = racers2[2, 12];
因為陣列是參照型別,所以將一個陣列變數賦予另一個陣列變數,就會得到兩個參照同一陣列的變數。
陣列實現ICloneable介面。這個介面定義的Clone()方法會複製陣列,建立陣列的淺表副本。
如果陣列的元素是值型別,Clone()方法會複製所有值:
int[] a1 = {1,2}; int[] a2 = (int[])a1.Clone();
如果陣列包含參照型別,只複製參照。
除了使用Clone()方法之外,還可以使用Array.Copy()方法建立淺表副本。
Person[] beatles = { new Person { FirstName="John", LastName="Lennon" }, new Person { FirstName="Paul", LastName="McCartney" } }; Person[] beatlesClone = (Person[])beatles.Clone(); Person[] beatlesClone2 = new Person[2]; Array.Copy(beatlesClone,beatlesClone2,2);//注意與Clone的語法區別,Copy需要傳遞階數相同的已有陣列。(還可以使用CopyTo()方法)
Array類使用快速排序演演算法對陣列中的元素進行排序。Sort()方法需要陣列中的元素實現IComparable介面。因為簡單型別(如String,Int32)實現IComparable介面,所以可以對包含這些型別的元素排序。
string[] names = { "Christina Aguilera", "Shakira", "Beyonce", "Gwen Stefani" }; Array.Sort(names); foreach (string name in names) { Console.WriteLine(name); }
如果對陣列使用使用自定義類,就必須實現IComparable介面。這個介面只定義了一個方法CompareTo()方法,如果要比較的物件相等,該方法就返回0.如果該範例應排在引數物件的前面,該方法就返回小於i0de值。如果該範例應排在引數物件的後面,該方法就返回大於0的值。
public class Person : IComparable<Person> { public string FirstName { get; set; } public string LastName { get; set; } public override string ToString() { return String.Format("{0} {1}", FirstName, LastName); } public int CompareTo(Person other) { if (other == null) throw new ArgumentNullException("other"); int result = this.LastName.CompareTo(other.LastName); if (result == 0) { result = this.FirstName.CompareTo(other.FirstName); } return result; } }
使用者端程式碼:
Person[] persons = { new Person { FirstName="Damon", LastName="Hill" }, new Person { FirstName="Niki", LastName="Lauda" }, new Person { FirstName="Ayrton", LastName="Senna" }, new Person { FirstName="Graham", LastName="Hill" } }; Array.Sort(persons); foreach (Person p in persons) { Console.WriteLine(p); }
如果Person物件的排序方式與上述不同,或者不能修改在陣列中用作元素的類,就可以實現IComparer介面或IComparer<T>介面。這兩個介面定義了方法Compare()方法。機型比較的類必須實現這兩個介面之一。
public enum PersonCompareType { FirstName, LastName } //通過使用實現了IComparer<T> 泛型介面的PersonComparer類比較Person物件陣列。 public class PersonComparer : IComparer<Person> { private PersonCompareType compareType; public PersonComparer(PersonCompareType compareType) { this.compareType = compareType; } #region IComparer<Person> Members public int Compare(Person x, Person y) { if (x == null) throw new ArgumentNullException("x"); if (y == null) throw new ArgumentNullException("y"); switch (compareType) { case PersonCompareType.FirstName: return x.FirstName.CompareTo(y.FirstName); case PersonCompareType.LastName: return x.LastName.CompareTo(y.LastName); default: throw new ArgumentException( "unexpected compare type"); } } #endregion }
使用者端程式碼:
Person[] persons = { new Person { FirstName="Damon", LastName="Hill" }, new Person { FirstName="Niki", LastName="Lauda" }, new Person { FirstName="Ayrton", LastName="Senna" }, new Person { FirstName="Graham", LastName="Hill" } }; Array.Sort(persons, new PersonComparer(PersonCompareType.FirstName)); foreach (Person p in persons) { Console.WriteLine(p); }
陣列可以作為引數傳遞給方法,也可以從方法中返回。
陣列支援協變。這表示陣列可以宣告為基礎類別,其派生型別的元素可以賦值於陣列元素。
static void DisPlay(object[] o) { //.. }
可以給該方法傳遞一個Person[]。
陣列協變只能用於參照型別,不能用於值型別。
結構ArraySegment<T>表示陣列的一段。如果需要使用不同的方法處理某個大型陣列的不同部分,那麼可以把相應的陣列部分複製到各個方法。
ArraySegment<T>結構包含了關於陣列段的資訊(偏移量和元素個數)。
static void Main() { int[] ar1 = { 1, 4, 5, 11, 13, 18 }; int[] ar2 = { 3, 4, 5, 18, 21, 27, 33 }; var segments = new ArraySegment<int>[2] { new ArraySegment<int>(ar1, 0, 3), new ArraySegment<int>(ar2, 3, 3) }; var sum = SumOfSegments(segments); Console.WriteLine("sum of all segments: {0}", sum); } static int SumOfSegments(ArraySegment<int>[] segments) { int sum = 0; foreach (var segment in segments) { for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) { sum += segment.Array[i]; } } return sum; }
陣列段不復制原陣列的元素,但原陣列可以通過ArraySegment<T>存取。如果陣列段中的元素改變了,這些變化就會反映到原陣列中。
在foreach語句中使用列舉,可以迭代集合中的元素,且無需知道集合中元素的個數。foreach語句使用一個列舉元。foreach會呼叫實現了IEnumerable介面的集合類中的GetEumerator()方法。GetEumerator()方法返回一個實現IEnumerator介面的物件列舉。foreach語句就可以使用IEnumerable介面迭代集合了。
GetEumerator()方法在IEnumerable介面中定義。
foreach語句使用IEnumerator介面的方法和屬性,迭代集合中所有元素。IEnumerator介面定義了Current屬性,來返回遊標所在的元素,該介面的MoveNext()方法移動到集合的下一個元素上,如果有這個元素,該方法就返回true。如果集合不再有更多的元素,該方法就返回false.
這個介面的泛型版本IEnumerator<T>派生自介面IDisposable,因此定義了Dispose()方法,來清理列舉元佔用的資源。
C#中foreach語句不會解析為IL程式碼中的foreach語句。C#編譯器會把foreach語句轉換為IEnumerator介面的方法和屬性。
Person[] persons = { new Person { FirstName="Damon", LastName="Hill" }, new Person { FirstName="Niki", LastName="Lauda" }, new Person { FirstName="Ayrton", LastName="Senna" }, new Person { FirstName="Graham", LastName="Hill" } }; foreach (Person p in persons) { Console.WriteLine(p); }
foreach語句會解析為下面的程式碼:
IEnumerator<Person> enumerator = persons.GetEumerator(); while(enumerator.MoveNext()) { Person p = enumerator.Current; Console.WriteLine(p); }
在C#2.0之前,foreach語句可以輕鬆的迭代集合,但建立列舉元需要做大量的工作。C#2.0新增了yield語句,以便建立列舉元。
yield return 語句返回集合的一個元素,並移動到下一個元素。yield break可停止迭代。
下面的例子實現返回兩個字串:
public class HelloCollection { public IEnumerator<string> GetEnumerator() { yield return "Hello"; yield return "World"; } }
使用者端程式碼:
var helloCollection = new HelloCollection(); foreach (string s in helloCollection) { Console.WriteLine(s); }
包含yield語句的方法或屬性也稱為迭代塊。迭代塊必須宣告為返回IEnumerator或IEnumerable介面,或者這些介面的泛型版本。這個塊可以包含多條yield return語句或yield break語句,但不能包含return語句。
使用迭代塊,編譯器會生成一個yield型別,其中包含一個狀態機,如下面程式碼所示:
yield型別實現IEnumerator和IDisposable介面的方法和屬性。下面的例子可以把yield型別看作內部類Enumerator。外部類的GetEnumerator()方法範例化並返回一個新的yield型別。在yield型別中,變數state定義了迭代的當前位置,每次呼叫MoveNext()時,當前位置都會改變。MoveNext()封裝了迭代塊的程式碼,並設定了current變數的值,從而使Current屬性根據位置返回一個物件。
public class HelloCollection { public IEnumerator<string> GetEnumerator() { return new Enumerator(0); } public class Enumerator:IEnumerator<string>,IEnumerator,IDisposable { private int state; private string current; public Enumerator(int state) { this.state = state; } bool System.Collections.IEnumerator.MoveNext() { switch(state) { case 0: current="hello"; state =1; return true; case 1: current="world"; state =2; return true; case 2: break; } return false; } void System.Collection>IEnumerator.Reset() { throw new NotSupportedException(); } string System.Collections.Generic.IEnumerator<string>.Current { get { return current; } } object System.Collections.IEnumerator.Current { get { return current; } } void IDisposable.Dispose() {} } }
yield語句會產生一個列舉元,而不僅僅生成一個包含的項的列表。這個列舉元通過foreach語句呼叫。從foreach中依次存取每一項,就會存取列舉元。這樣就可以迭代大量的資料,而無需一次把所有的資料都讀入記憶體。
可以使用yield return語句,以不同方式迭代集合。
類MusicTitles可以用預設方式通過GetEnumerator()方法迭代標題,該方法不必在程式碼中編寫,也可以用Reverse()逆序迭代標題,用Subset()方法迭代子集合:
public class MusicTitles { string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" }; public IEnumerator<string> GetEnumerator() { for (int i = 0; i < 4; i++) { yield return names[i]; } } public IEnumerable<string> Reverse() { for (int i = 3; i >= 0; i--) { yield return names[i]; } } public IEnumerable<string> Subset(int index, int length) { for (int i = index; i < index + length;i++) { yield return names[i]; } } }
使用者端程式碼:
var titles = new MusicTitles(); foreach (var title in titles) { Console.WriteLine(title); } Console.WriteLine(); Console.WriteLine("reverse"); foreach (var title in titles.Reverse()) { Console.WriteLine(title); } Console.WriteLine(); Console.WriteLine("subset"); foreach (var title in titles.Subset(2, 2)) { Console.WriteLine(title); }
public class GameMoves { private IEnumerator cross; private IEnumerator circle; public GameMoves() { cross = Cross(); circle = Circle(); } private int move = 0; const int MaxMoves = 9; public IEnumerator Cross() { while (true) { Console.WriteLine("Cross, move {0}", move); if (++move >= MaxMoves) yield break; yield return circle; } } public IEnumerator Circle() { while (true) { Console.WriteLine("Circle, move {0}", move); if (++move >= MaxMoves) yield break; yield return cross; } } }
使用者端程式碼:
var game = new GameMoves(); IEnumerator enumerator = game.Cross(); while (enumerator.MoveNext()) { enumerator = enumerator.Current as IEnumerator; }
這樣會交替呼叫Cross()和Circle()方法。
元組可以合併不同型別的物件。元組起源於函式程式語言,如F#。在.NET Framework中,元組可用於所有的.Net語言。
.NET Framework定義了8個泛型Tuple類和一個靜態Tuple類,它們用作元組的工廠。不同的泛型Tuple類支援不同數量的元素。如,Tuple<T1>包含一個元素,Tuple<T1,T2>包含兩個元素。
Tuple<string, string> name = new Tuple<string, string>("Jochen", "Rindt");
元組也可以用靜態Tuple類的靜態Create()方法建立。Create()方法的泛型引數定了要範例化的元組型別:
public static Tuple<int, int> Divide(int dividend, int divisor) { int result = dividend / divisor; int reminder = dividend % divisor; return Tuple.Create<int, int>(result, reminder); }
可以用屬性Item1和Item2存取元組的項:
var result = Divide(5, 2); Console.WriteLine("result of division: {0}, reminder: {1}", result.Item1, result.Item2);
如果元組包含的項超過8個,就可以使用帶8個引數的Tuple類定義。最後一個模板引數是TRest,表示必須給它傳遞一個元組。這樣,就可以建立帶任意個引數的元組了。
var tuple = Tuple.Create<string, string, string, int, int, int, double, Tuple<int, int>>( "Stephanie", "Alina", "Nagel", 2009, 6, 2, 1.37, Tuple.Create<int, int>(52, 3490));
陣列和元組都實現介面IStructuralEquatable和IStructuralComparable。這兩個介面不僅可以比較參照,還可以比較內容。這些介面都是顯式實現的,所以在使用時需要把陣列和元組強制轉換為這個介面。
IStructuralEquatable介面用於比較兩個元組或陣列是否有相同的內同,IStructuralComparable介面用於給元組或陣列排序。
IStructuralEquatable介面範例:
編寫實現IEquatable介面的Person類,IEquatable介面定義了一個強型別化的Equals()方法,比較FirstName和LastName的值:
public class Person : IEquatable<Person> { public int Id { get; private set; } public string FirstName { get; set; } public string LastName { get; set; } public override string ToString() { return String.Format("{0}, {1} {2}", Id, FirstName, LastName); } public override bool Equals(object obj) { if (obj == null) return base.Equals(obj); return Equals(obj as Person); } public override int GetHashCode() { return Id.GetHashCode(); } #region IEquatable<Person> Members public bool Equals(Person other) { if (other == null) return base.Equals(other); return this.FirstName == other.FirstName && this.LastName == other.LastName; } #endregion }
建立兩個包含相同內容的Person型別的陣列:
var janet = new Person { FirstName = "Janet", LastName = "Jackson" }; Person[] persons1 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet }; Person[] persons2 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };
由於兩個變數參照兩個不同陣列,所以!=返回True:
if (persons1 != persons2) Console.WriteLine("not the same reference");
對於IStructuralEquatable介面定義的Equals方法,第一個引數是object型別,第二個引數是IEqualityComparer型別。呼叫這個方法時,通過傳遞一個實現了EqualityComparer<T>的物件,就可以定義如何進行比較。通過EqualityComparer<T>類完成IEqualityComparer的一個預設實現。這個實現檢查T型別是否實現了IEquatable介面,並呼叫IEquatable.Equals()方法。如果該類沒有實現IEquatable介面,就呼叫Object基礎類別中Equals()方法:
if ((persons1 as IStructuralEquatable).Equals(persons2, EqualityComparer<Person>.Default)) { Console.WriteLine("the same content"); }
元組範例:
Tuple<>類提供了兩個Epuals()方法:一個重寫了Object基礎類別中的Epuals方法,並把object作為引數,第二個由IStructuralEquatable介面定義,並把object和IEqualityComparer作為引數。
var t1 = Tuple.Create<int, string>(1, "Stephanie"); var t2 = Tuple.Create<int, string>(1, "Stephanie"); if (t1 != t2) Console.WriteLine("not the same reference to the tuple");
這個方法使用EqualityComparer<object>.Default獲取一個ObjectEqualityComparer<object>,以進行比較。這樣就會呼叫Object.Equals()方法比較元組的每一項:
if (t1.Equals(t2)) Console.WriteLine("equals returns true");
還可以使用TupleComparer類建立一個自定義的IEqualityComparer
TupleComparer tc = new TupleComparer(); if ((t1 as IStructuralEquatable).Equals(t2, tc)) { Console.WriteLine("yes, using TubpleComparer"); } class TupleComparer : IEqualityComparer { #region IEqualityComparer Members public new bool Equals(object x, object y) { bool result = x.Equals(y); return result; } public int GetHashCode(object obj) { return obj.GetHashCode(); } #endregion }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45