物件和集合初始設定式 (C# 程式設計手冊)

C# 可讓您具現化物件或集合,並以單一陳述式執行成員指派。

物件初始設定式

物件初始設定式可讓您在建立期間將值指派給物件的任何可存取欄位或屬性,而不用叫用後面接著幾行指派陳述式的建構函式。 物件初始設定式語法可讓您為建構函式指定引數或省略引數 (以及括號語法)。 下列範例將示範如何使用有具名型別 Cat 的物件初始設定式,以及如何叫用無參數建構函式。 請注意 Cat 類別中自動實作屬性的用法。 如需詳細資訊,請參閱自動實作的屬性

public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
        this.Name = name;
    }
}
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };

物件初始設定式語法可讓您建立執行個體,然後將新建立的物件及其指派的屬性指派給指派作業中的變數。

除了指派欄位和屬性之外,物件初始化運算式還可以設定索引子。 請考慮此基本 Matrix 類別:

public class Matrix
{
    private double[,] storage = new double[3, 3];

    public double this[int row, int column]
    {
        // The embedded array will throw out of range exceptions as appropriate.
        get { return storage[row, column]; }
        set { storage[row, column] = value; }
    }
}

您可以使用下列程式碼來初始化單位矩陣:

var identity = new Matrix
{
    [0, 0] = 1.0,
    [0, 1] = 0.0,
    [0, 2] = 0.0,

    [1, 0] = 0.0,
    [1, 1] = 1.0,
    [1, 2] = 0.0,

    [2, 0] = 0.0,
    [2, 1] = 0.0,
    [2, 2] = 1.0,
};

任何包含可存取 setter 的可存取索引子都可作為物件初始設定式中的其中一個運算式,不論引數的數目或類型為何。 指派左側是由索引引數組成,運算式右側是其值。 例如,如果 IndexersExample 具有適當的索引子,下列內容全部有效:

var thing = new IndexersExample {
    name = "object one",
    [1] = '1',
    [2] = '4',
    [3] = '9',
    Size = Math.PI,
    ['C',4] = "Middle C"
}

若要編譯上述程式碼,IndexersExample 類型必須具有下列成員:

public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] {  set { ... }; }

具有匿名類型的物件初始設定式

雖然物件初始化運算式可用於任何內容,但在 LINQ 查詢運算式中特別有用。 查詢運算式經常使用匿名型別,此型別只能使用物件初始設定式初始化,如下列宣告所示。

var pet = new { Age = 10, Name = "Fluffy" };  

匿名型別可讓 select LINQ 查詢運算式中的 子句將原始序列的物件轉換成值和圖形可能與原始物件不同的物件。 如果您只要儲存序列中每個物件的部分資訊,這就會很實用。 在下列範例中,假設產品物件 (p) 包含許多欄位和方法,而您只想要建立包含產品名稱和單價的物件序列。

var productInfos =
    from p in products
    select new { p.ProductName, p.UnitPrice };

執行此查詢時,productInfos 變數將會包含可以在 foreach 陳述式中存取的物件序列,如下列範例所示:

foreach(var p in productInfos){...}  

新的匿名型別中的每個物件都有兩個公用屬性,這兩個屬性會接收與原始物件中的屬性或欄位相同的名稱。 您也可以在建立匿名類型時重新命名欄位,下列範例會將 UnitPrice 欄位重新命名為 Price

select new {p.ProductName, Price = p.UnitPrice};  

集合初始設定式

如果集合類別是實作 IEnumerable,且具有 Add 與適當簽章以作為執行個體方法或擴充方法,集合初始設定式可讓您在初始化這類集合類別時,指定一或多個項目初始設定式。 項目初始設定式可以是簡單的值、運算式或物件初始設定式。 藉由使用集合初始設定式,您就不需要指定多個呼叫,編譯器會自動新增呼叫。

下列範例將示範兩個簡單的集合初始設定式:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
List<int> digits2 = new List<int> { 0 + 1, 12 % 3, MakeInt() };  

下列集合初始設定式會使用物件初始設定式來初始化先前範例中所定義 Cat 類別的物件。 請注意,每個物件初始設定式會以括號括住並以逗號分隔。

List<Cat> cats = new List<Cat>
{
    new Cat{ Name = "Sylvester", Age=8 },
    new Cat{ Name = "Whiskers", Age=2 },
    new Cat{ Name = "Sasha", Age=14 }
};

如果集合的 Add 方法允許,您可以將 null 指定為集合初始設定式中的項目。

List<Cat> moreCats = new List<Cat>
{
    new Cat{ Name = "Furrytail", Age=5 },
    new Cat{ Name = "Peaches", Age=4 },
    null
};

如果集合支援讀取/寫入索引,您可以指定索引的項目。

var numbers = new Dictionary<int, string>
{
    [7] = "seven",
    [9] = "nine",
    [13] = "thirteen"
};

上述範例會產生呼叫 Item[TKey] 以設定值的程式碼。 您也可以使用下列語法,初始化字典和其他關聯容器。 請注意,它會使用具有多個值的物件,而不是使用括弧和指派的索引子語法:

var moreNumbers = new Dictionary<int, string>
{
    {19, "nineteen" },
    {23, "twenty-three" },
    {42, "forty-two" }
};

初始設定式範例會呼叫 Add(TKey, TValue) 來將三個項目新增至字典。 由於編譯器所產生的方法呼叫,這兩種初始化關聯集合的方式會有稍微不同的行為。 這兩種方式都可以搭配 Dictionary 類別運作。 其他類型視其公用 API 而定,可能只支援其中一種。

具有集合唯讀屬性初始化的物件初始化運算式

某些類別可能有集合屬性,其中屬性是唯讀的,如 Cats 下列案例中的 屬性 CatOwner

public class CatOwner
{
    public IList<Cat> Cats { get; } = new List<Cat>();
}

您無法使用到目前為止討論的集合初始化運算式語法,因為屬性無法指派新的清單:

CatOwner owner = new CatOwner
{
    Cats = new List<Cat>
    {
        new Cat{ Name = "Sylvester", Age=8 },
        new Cat{ Name = "Whiskers", Age=2 },
        new Cat{ Name = "Sasha", Age=14 }
    }
};

不過,藉由省略清單建立 () new List<Cat> ,仍然可以使用初始化語法,將新專案新增至 Cats ,如下所示:

CatOwner owner = new CatOwner
{
    Cats =
    {
        new Cat{ Name = "Sylvester", Age=8 },
        new Cat{ Name = "Whiskers", Age=2 },
        new Cat{ Name = "Sasha", Age=14 }
    }
};

要加入的專案集只會以大括弧括住。 上述內容與寫入相同:

CatOwner owner = new CatOwner();
owner.Cats.Add(new Cat{ Name = "Sylvester", Age=8 });
owner.Cats.Add(new Cat{ Name = "Whiskers", Age=2 });
owner.Cats.Add(new Cat{ Name = "Sasha", Age=14 });

範例

下列範例結合了物件和集合初始設定式的概念。

public class InitializationSample
{
    public class Cat
    {
        // Auto-implemented properties.
        public int Age { get; set; }
        public string Name { get; set; }

        public Cat() { }

        public Cat(string name)
        {
            Name = name;
        }
    }

    public static void Main()
    {
        Cat cat = new Cat { Age = 10, Name = "Fluffy" };
        Cat sameCat = new Cat("Fluffy"){ Age = 10 };

        List<Cat> cats = new List<Cat>
        {
            new Cat { Name = "Sylvester", Age = 8 },
            new Cat { Name = "Whiskers", Age = 2 },
            new Cat { Name = "Sasha", Age = 14 }
        };

        List<Cat> moreCats = new List<Cat>
        {
            new Cat { Name = "Furrytail", Age = 5 },
            new Cat { Name = "Peaches", Age = 4 },
            null
        };

        // Display results.
        System.Console.WriteLine(cat.Name);

        foreach (Cat c in cats)
            System.Console.WriteLine(c.Name);

        foreach (Cat c in moreCats)
            if (c != null)
                System.Console.WriteLine(c.Name);
            else
                System.Console.WriteLine("List element has null value.");
    }
    // Output:
    //Fluffy
    //Sylvester
    //Whiskers
    //Sasha
    //Furrytail
    //Peaches
    //List element has null value.
}

下列範例顯示實作 IEnumerable 並包含具有多個參數之 Add 方法的物件,它使用集合初始設定式來處理對應至 Add 方法簽章之清單中每個項目的多個元素。

    public class FullExample
    {
        class FormattedAddresses : IEnumerable<string>
        {
            private List<string> internalList = new List<string>();
            public IEnumerator<string> GetEnumerator() => internalList.GetEnumerator();

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalList.GetEnumerator();

            public void Add(string firstname, string lastname,
                string street, string city,
                string state, string zipcode) => internalList.Add(
                $@"{firstname} {lastname}
{street}
{city}, {state} {zipcode}"
                );
        }

        public static void Main()
        {
            FormattedAddresses addresses = new FormattedAddresses()
            {
                {"John", "Doe", "123 Street", "Topeka", "KS", "00000" },
                {"Jane", "Smith", "456 Street", "Topeka", "KS", "00000" }
            };

            Console.WriteLine("Address Entries:");

            foreach (string addressEntry in addresses)
            {
                Console.WriteLine("\r\n" + addressEntry);
            }
        }

        /*
         * Prints:

            Address Entries:

            John Doe
            123 Street
            Topeka, KS 00000

            Jane Smith
            456 Street
            Topeka, KS 00000
         */
    }

Add 方法可以使用 params 關鍵字來接受各種數目的引數,如下列範例所示。 此範例也示範索引子的自訂實作,以使用索引來初始化集合。

public class DictionaryExample
{
    class RudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>>
    {
        private Dictionary<TKey, List<TValue>> internalDictionary = new Dictionary<TKey, List<TValue>>();

        public IEnumerator<KeyValuePair<TKey, List<TValue>>> GetEnumerator() => internalDictionary.GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalDictionary.GetEnumerator();

        public List<TValue> this[TKey key]
        {
            get => internalDictionary[key];
            set => Add(key, value);
        }

        public void Add(TKey key, params TValue[] values) => Add(key, (IEnumerable<TValue>)values);

        public void Add(TKey key, IEnumerable<TValue> values)
        {
            if (!internalDictionary.TryGetValue(key, out List<TValue> storedValues))
                internalDictionary.Add(key, storedValues = new List<TValue>());

            storedValues.AddRange(values);
        }
    }

    public static void Main()
    {
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary1
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                {"Group1", "Bob", "John", "Mary" },
                {"Group2", "Eric", "Emily", "Debbie", "Jesse" }
            };
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary2
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                ["Group1"] = new List<string>() { "Bob", "John", "Mary" },
                ["Group2"] = new List<string>() { "Eric", "Emily", "Debbie", "Jesse" }
            };
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary3
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                {"Group1", new string []{ "Bob", "John", "Mary" } },
                { "Group2", new string[]{ "Eric", "Emily", "Debbie", "Jesse" } }
            };

        Console.WriteLine("Using first multi-valued dictionary created with a collection initializer:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary1)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }

        Console.WriteLine("\r\nUsing second multi-valued dictionary created with a collection initializer using indexing:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary2)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }
        Console.WriteLine("\r\nUsing third multi-valued dictionary created with a collection initializer using indexing:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary3)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }
    }

    /*
     * Prints:

        Using first multi-valued dictionary created with a collection initializer:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse

        Using second multi-valued dictionary created with a collection initializer using indexing:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse

        Using third multi-valued dictionary created with a collection initializer using indexing:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse
     */
}

另請參閱