Inicjatory obiektów i kolekcji (Przewodnik programowania w języku C#)

Język C# umożliwia tworzenie wystąpień obiektu lub kolekcji i wykonywanie przypisań elementów członkowskich w jednej instrukcji.

Inicjatory obiektów

Inicjatory obiektów umożliwiają przypisywanie wartości do dowolnych dostępnych pól lub właściwości obiektu w czasie jego tworzenia, bez konieczności wywoływania konstruktora, po którym występują wiersze instrukcji przypisania. Składnia inicjatora obiektów umożliwia określenie argumentów dla konstruktora lub pominięcie argumentów (i składni z nawiasami). W poniższym przykładzie pokazano, jak używać inicjatora obiektów z nazwanym typem i Cat jak wywoływać konstruktor bezparametrowy. Zwróć uwagę na użycie automatycznie implementowane właściwości w Cat klasie . Aby uzyskać więcej informacji, zobacz Właściwości zaimplementowane automatycznie.

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 };

Składnia inicjatorów obiektów umożliwia utworzenie wystąpienia, a następnie przypisanie nowo utworzonego obiektu z przypisanymi właściwościami do zmiennej w przypisaniu.

Począwszy od języka C# 6, inicjatory obiektów mogą ustawiać indeksatory, oprócz przypisywania pól i właściwości. Rozważmy tę Matrix podstawową klasę:

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; }
    }
}

Macierz tożsamości można zainicjować przy użyciu następującego kodu:

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,
};

Każdy dostępny indeksator, który zawiera dostępny element ustawiający, może być używany jako jedno z wyrażeń w inicjatorze obiektów, niezależnie od liczby lub typów argumentów. Argumenty indeksu tworzą lewą stronę przypisania, a wartość jest po prawej stronie wyrażenia. Na przykład wszystkie te wartości są prawidłowe, IndexersExample jeśli mają odpowiednie indeksatory:

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

Aby powyższy kod został skompilowany, IndexersExample typ musi mieć następujące elementy członkowskie:

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

Inicjatory obiektów z typami anonimowymi

Chociaż inicjatory obiektów mogą być używane w dowolnym kontekście, są one szczególnie przydatne w wyrażeniach zapytań LINQ. Wyrażenia zapytań często używają anonimowych typów ,które można zainicjować tylko przy użyciu inicjatora obiektów, jak pokazano w poniższej deklaracji.

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

Typy anonimowe umożliwiają klauzuli w wyrażeniu zapytania LINQ przekształcanie obiektów oryginalnej sekwencji w obiekty, których wartość i kształt mogą różnić się select od oryginalnych. Jest to użyteczne, gdy chce się przechowywać tylko część informacji z każdego obiektu sekwencji. W poniższym przykładzie przyjęto założenie, że obiekt produktu ( ) zawiera wiele pól i metod oraz że interesuje Cię tylko utworzenie sekwencji obiektów, które zawierają nazwę produktu i cenę p jednostkową.

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

Po wykonaniu tego zapytania zmienna będzie zawierać sekwencję obiektów, do których można uzyskać dostęp w instrukcji , jak productInfos foreach pokazano w poniższym przykładzie:

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

Każdy obiekt w nowym typie anonimowym ma dwie właściwości publiczne, które otrzymują takie same nazwy jak właściwości lub pola w oryginalnym obiekcie. Nazwę pola można również zmienić podczas tworzenia typu anonimowego. W poniższym przykładzie zmieniono UnitPrice nazwę pola na Price .

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

Inicjatory kolekcji

Inicjatory kolekcji umożliwiają określenie co najmniej jednego inicjatora elementu podczas inicjowania typu kolekcji, który implementuje i ma odpowiedni podpis jako metodę wystąpienia lub IEnumerable Add metodę rozszerzenia. Inicjatory elementów mogą być prostą wartością, wyrażeniem lub inicjatorem obiektu. Za pomocą inicjatora kolekcji nie trzeba określać wielu wywołań; Kompilator dodaje wywołania automatycznie.

W poniższym przykładzie przedstawiono dwa proste inicjatory kolekcji:

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() };  

Poniższy inicjator kolekcji używa inicjatorów obiektów do inicjowania obiektów Cat klasy zdefiniowanej w poprzednim przykładzie. Należy zauważyć, że poszczególne inicjatory obiektów są umieszczone w nawiasach klamrowych i rozdzielone przecinkami.

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

Jeśli metoda kolekcji na to zezwala, można określić wartość null jako element w inicjatorze Add kolekcji.

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

Możesz określić indeksowane elementy, jeśli kolekcja obsługuje indeksowanie odczytu/zapisu.

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

Powyższy przykład generuje kod, który wywołuje , Item[TKey] aby ustawić wartości. Przed kodem C# 6 można było zainicjować słowniki i inne kontenery asocjacyjne przy użyciu następującej składni. Zwróć uwagę, że zamiast składni indeksatora, z nawiasami i przypisaniem, używa ona obiektu z wieloma wartościami:

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

Ten przykład Add(TKey, TValue) inicjatora wywołuje w celu dodania trzech elementów do słownika. Te dwa różne sposoby inicjowania kolekcji asocjacyjnych mają nieco inne zachowanie ze względu na wywołania metody generowane przez kompilator. Oba warianty działają z Dictionary klasą . Inne typy mogą obsługiwać tylko jeden z nich na podstawie ich publicznego interfejsu API.

Inicjatory obiektów z inicjacją właściwości tylko do odczytu kolekcji

Niektóre klasy mogą mieć właściwości kolekcji, w których właściwość jest tylko do odczytu, podobnie jak właściwość klasy Cats CatOwner w następującym przypadku:

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

Nie będzie można używać omówionej do tej pory składni inicjatora kolekcji, ponieważ nie można przypisać nowej listy właściwości:

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 }
    }
};

Jednak nowe wpisy mogą być dodawane do niemniej jednak przy użyciu składni inicjowania, pomijając tworzenie listy Cats ( new List<Cat> ), jak pokazano poniżej:

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

Zestaw wpisów do dodania jest po prostu wyświetlany w nawiasach klamrowych. Powyższe są identyczne jak w przypadku pisania:

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 });

Przykłady

Poniższy przykład łączy koncepcje inicjatorów obiektów i kolekcji.

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.
}

W poniższym przykładzie pokazano obiekt, który implementuje i zawiera metodę z wieloma parametrami. Używa inicjatora kolekcji z wieloma elementami na liście, które odpowiadają podpisowi IEnumerable Add Add metody.

    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 Metody mogą używać słowa params kluczowego , aby przyjmować zmienną liczbę argumentów, jak pokazano w poniższym przykładzie. W tym przykładzie pokazano również niestandardową implementację indeksatora w celu zainicjowania kolekcji przy użyciu indeksów.

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
     */
}

Zobacz też