Megosztás a következőn keresztül:


Objektum- és gyűjtemény inicializálók (C# programozási útmutató)

A C# lehetővé teszi egy objektum vagy gyűjtemény példányosítását és tag-hozzárendelések egyetlen utasításban történő végrehajtását.

Objektum-inicializálók

Az objektum inicializálói lehetővé teszik értékek hozzárendelését egy objektum bármely akadálymentes mezőjéhez vagy tulajdonságához a létrehozáskor anélkül, hogy konstruktort kellene meghívnia, amelyet a hozzárendelési utasítások sorai követnek. Az objektum inicializáló szintaxisa lehetővé teszi egy konstruktor argumentumainak megadását, vagy kihagyhatja az argumentumokat (és zárójelek szintaxisát). Az alábbi példa bemutatja, hogyan használható egy objektum inicializáló nevesített típussal, Cat és hogyan hívhatja meg a paraméter nélküli konstruktort. Figyelje meg az automatikus tulajdonságok használatát az Cat osztályban. További információ: Automatikusan implementált tulajdonságok.

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

Az objektum-inicializálók szintaxisa lehetővé teszi egy példány létrehozását, és ezt követően hozzárendeli az újonnan létrehozott objektumot a hozzárendelt tulajdonságokat tartalmazó változóhoz a hozzárendelésben.

Az objektum inicializálói a mezők és tulajdonságok hozzárendelése mellett indexelőket is beállíthatnak. Fontolja meg ezt az alapszintű Matrix osztályt:

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

Az identitásmátrixot a következő kóddal inicializálhatja:

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

Bármely akadálymentes halmazt tartalmazó akadálymentes indexelő használható az objektum inicializálójának egyik kifejezéseként, függetlenül az argumentumok számától vagy típusától. Az indexargumentumok a hozzárendelés bal oldalát alkotják, az érték pedig a kifejezés jobb oldala. Például a következő inicializálók érvényesek, ha IndexersExample a megfelelő indexelőkkel rendelkezik:

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

Az előző kód fordításához a IndexersExample típusnak a következő tagokat kell tartalmaznia:

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

Névtelen típusok objektum-inicializálói

Bár az objektum-inicializálók bármilyen környezetben használhatók, különösen hasznosak a LINQ lekérdezési kifejezésekben. A lekérdezési kifejezések gyakran használnak névtelen típusokat, amelyeket csak objektum inicializálóval lehet inicializálni, ahogyan az az alábbi deklarációban is látható.

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

A névtelen típusok lehetővé teszik, hogy a select LINQ-lekérdezési kifejezések záradéka az eredeti sorozat objektumait olyan objektumokká alakítsa, amelyek értéke és alakja eltérhet az eredetitől. Előfordulhat, hogy az egyes objektumok információinak csak egy részét szeretné sorozatban tárolni. A következő példában tegyük fel, hogy egy termékobjektum (p) számos mezőt és metódust tartalmaz, és csak a terméknevet és az egységárat tartalmazó objektumok sorozatát szeretné létrehozni.

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

A lekérdezés végrehajtásakor a productInfos változó olyan objektumok sorozatát tartalmazza, amelyek az alábbi példában látható módon érhetők el egy foreach utasításban:

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

Az új névtelen típus minden objektuma két nyilvános tulajdonsággal rendelkezik, amelyek ugyanazokat a neveket kapják, mint az eredeti objektum tulajdonságai vagy mezői. Névtelen típus létrehozásakor is átnevezhet egy mezőt; az alábbi példa átnevezi a mezőt a UnitPrice következőre Price: .

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

Objektum-inicializálók a required módosítóval

A kulcsszóval kényszerítheti a required hívókat egy tulajdonság vagy mező értékének objektum inicializálóval való beállítására. A szükséges tulajdonságokat nem kell konstruktorparaméterként beállítani. A fordító biztosítja, hogy az összes hívó inicializálja ezeket az értékeket.

public class Pet
{
    public required int Age;
    public string Name;
}

// `Age` field is necessary to be initialized.
// You don't need to initialize `Name` property
var pet = new Pet() { Age = 10};

// Compiler error:
// Error CS9035 Required member 'Pet.Age' must be set in the object initializer or attribute constructor.
// var pet = new Pet();

Ez egy tipikus gyakorlat, amely garantálja, hogy az objektum megfelelően inicializálva van, különösen akkor, ha több mezőt vagy tulajdonságot szeretne kezelni, és nem szeretné mindet belefoglalni a konstruktorba.

Objektum-inicializálók a init tartozékkal

Győződjön meg arról, hogy a tervezett objektumot senki sem módosíthatja, ha tartozékot init használ. Segít korlátozni a tulajdonságérték beállítását.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; init; }
}

// The `LastName` property can be set only during initialization. It CAN'T be modified afterwards.
// The `FirstName` property can be modified after initialization.
var pet = new Person() { FirstName = "Joe", LastName = "Doe"};

// You can assign the FirstName property to a different value.
pet.FirstName = "Jane";

// Compiler error:
// Error CS8852  Init - only property or indexer 'Person.LastName' can only be assigned in an object initializer,
//               or on 'this' or 'base' in an instance constructor or an 'init' accessor.
// pet.LastName = "Kowalski";

A kötelező csak init-only tulajdonságok támogatják a nem módosítható struktúrákat, miközben lehetővé teszik a természetes szintaxist a típus felhasználói számára.

Osztálytípusú tulajdonságokkal rendelkező objektum-inicializálók

Az objektum inicializálása során fontos figyelembe venni az osztálytípusú tulajdonságok következményeit:

public class HowToClassTypedInitializer
{
    public class EmbeddedClassTypeA
    {
        public int I { get; set; }
        public bool B { get; set; }
        public string S { get; set; }
        public EmbeddedClassTypeB ClassB { get; set; }

        public override string ToString() => $"{I}|{B}|{S}|||{ClassB}";

        public EmbeddedClassTypeA()
        {
            Console.WriteLine($"Entering EmbeddedClassTypeA constructor. Values are: {this}");
            I = 3;
            B = true;
            S = "abc";
            ClassB = new() { BB = true, BI = 43 };
            Console.WriteLine($"Exiting EmbeddedClassTypeA constructor. Values are: {this})");
        }
    }

    public class EmbeddedClassTypeB
    {
        public int BI { get; set; }
        public bool BB { get; set; }
        public string BS { get; set; }

        public override string ToString() => $"{BI}|{BB}|{BS}";

        public EmbeddedClassTypeB()
        {
            Console.WriteLine($"Entering EmbeddedClassTypeB constructor. Values are: {this}");
            BI = 23;
            BB = false;
            BS = "BBBabc";
            Console.WriteLine($"Exiting EmbeddedClassTypeB constructor. Values are: {this})");
        }
    }

    public static void Main()
    {
        var a = new EmbeddedClassTypeA
        {
            I = 103,
            B = false,
            ClassB = { BI = 100003 }
        };
        Console.WriteLine($"After initializing EmbeddedClassTypeA: {a}");

        var a2 = new EmbeddedClassTypeA
        {
            I = 103,
            B = false,
            ClassB = new() { BI = 100003 } //New instance
        };
        Console.WriteLine($"After initializing EmbeddedClassTypeA a2: {a2}");
    }

    // Output:
    //Entering EmbeddedClassTypeA constructor Values are: 0|False||||
    //Entering EmbeddedClassTypeB constructor Values are: 0|False|
    //Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
    //Exiting EmbeddedClassTypeA constructor Values are: 3|True|abc|||43|True|BBBabc)
    //After initializing EmbeddedClassTypeA: 103|False|abc|||100003|True|BBBabc
    //Entering EmbeddedClassTypeA constructor Values are: 0|False||||
    //Entering EmbeddedClassTypeB constructor Values are: 0|False|
    //Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
    //Exiting EmbeddedClassTypeA constructor Values are: 3|True|abc|||43|True|BBBabc)
    //Entering EmbeddedClassTypeB constructor Values are: 0|False|
    //Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
    //After initializing EmbeddedClassTypeA a2: 103|False|abc|||100003|False|BBBabc
}

Az alábbi példa bemutatja, hogy a ClassB esetében az inicializálási folyamat hogyan foglalja magában bizonyos értékek frissítését, miközben másokat is megtart az eredeti példányból. Az Inicializáló újra felhasználja az aktuális példányt: A ClassB értékei a következők: 100003 (az itt hozzárendelt új érték), true (az EmbeddedClassTypeA inicializálásából megtartva), BBBabc (az EmbeddedClassTypeB alapértelmezett értéke változatlan).

Gyűjtemény inicializálói

A gyűjtemény inicializálói lehetővé teszik egy vagy több elem inicializálójának megadását egy implementáló IEnumerable és Add a megfelelő aláírással rendelkező gyűjteménytípus inicializálásakor példány- vagy bővítménymetódusként. Az elem-inicializálók lehetnek értékek, kifejezések vagy objektum inicializálók. Gyűjtemény inicializáló használatával nem kell több hívást megadnia; a fordító automatikusan hozzáadja a hívásokat.

Az alábbi példa két egyszerű gyűjtemény inicializálót mutat be:

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

Az alábbi gyűjtemény inicializálója objektum-inicializálókkal inicializálja az Cat előző példában definiált osztály objektumait. Az egyes objektum inicializálók zárójelek közé vannak zárva, és vesszővel vannak elválasztva.

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

Ha a gyűjtemény metódusa lehetővé teszi, null értéket adhat meg elemként a gyűjtemény inicializálójábanAdd.

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

Indexelt elemeket akkor adhat meg, ha a gyűjtemény támogatja az olvasási/írási indexelést.

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

Az előző minta olyan kódot hoz létre, amely meghívja az Item[TKey] értékeket. A szótárakat és más asszociatív tárolókat az alábbi szintaxissal is inicializálhatja. Figyelje meg, hogy az indexelő szintaxis helyett zárójelekkel és hozzárendeléssel egy több értékkel rendelkező objektumot használ:

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

Ez az inicializáló példa meghívja Add(TKey, TValue) , hogy adja hozzá a három elemet a szótárhoz. Az asszociatív gyűjtemények inicializálásának két különböző módja kissé eltérő viselkedést eredményez a fordító által generált metódushívások miatt. Mindkét változat együttműködik az Dictionary osztálysal. Más típusok csak az egyiket vagy a másikat támogatják a nyilvános API-juk alapján.

Objektum-inicializálók gyűjtemény írásvédett tulajdonság inicializálásával

Egyes osztályok olyan gyűjteménytulajdonságokkal rendelkezhetnek, amelyekben a tulajdonság írásvédett, például az CatsCatOwner alábbi esetben:

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

Az eddig tárgyalt gyűjtemény inicializáló szintaxisa nem használható, mivel a tulajdonság nem rendelhető hozzá új listához:

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

Az inicializálási szintaxissal azonban új bejegyzések is hozzáadhatók Cats a listalétrehozás kihagyásával (new List<Cat>a következő módon:

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

A hozzáadni kívánt bejegyzések kapcsos zárójelek között jelennek meg. Az előző kód megegyezik az írással:

CatOwner owner = new ();
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 });

Példák

Az alábbi példa egyesíti az objektum- és gyűjtemény-inicializálók fogalmait.

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

Az alábbi példa egy olyan objektumot mutat be, amely több paramétert tartalmazó metódust Add implementál IEnumerable és tartalmaz. Egy gyűjtemény inicializálóját használja, amely elemenként több elemet tartalmaz a listában, amelyek megfelelnek a Add metódus aláírásának.

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 metódusok a params kulcsszóval változó számú argumentumot használhatnak, ahogy az az alábbi példában is látható. Ez a példa azt is bemutatja, hogy egy indexelő egyéni implementációja alapján inicializálhat egy gyűjteményt indexekkel. A C# 13-tól kezdődően a params paraméter nem korlátozódik tömbre. Ez lehet gyűjteménytípus vagy felület.

public class DictionaryExample
{
    class RudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>> where TKey : notnull
    {
        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
        */
}

Lásd még