Instantier objekter ved hjælp af initialiserings- og kopikonstruktører
Objektinitialiseringsfunktionen og kopikonstruktørerne er to måder at instantiere objekter på i C#. Objektinitialisering giver dig mulighed for at tildele værdier til alle tilgængelige felter eller egenskaber for et objekt på oprettelsestidspunktet uden at skulle aktivere en konstruktør efterfulgt af linjer med tildelingssætninger. Kopiér konstruktører giver dig mulighed for at oprette et nyt objekt ved at kopiere værdierne for et eksisterende objekt.
Brug af objektinitialiseringsmaskiner og kopikonstruktører kan hjælpe dig med at skrive mere præcis og læsbar kode.
Initialisering af objekt
Med objektinitialiseringsfunktionen kan du tildele værdier til alle tilgængelige felter eller egenskaber for et objekt på oprettelsestidspunktet uden at skulle aktivere en konstruktør efterfulgt af linjer med tildelingssætninger. Objektinitialiseringssyntaksen giver dig mulighed for at angive argumenter for en konstruktør eller udelade argumenterne (og parentessyntaksen).
Du kan bruge objektinitialisering til at initialisere typeobjekter på en deklarativ måde uden eksplicit at aktivere en konstruktør for typen.
Compileren behandler objektinitialiseringsfunktionen ved først at få adgang til konstruktøren for den parameterløse forekomst og derefter behandle medlemsinitialiseringerne. Hvis den parameterløse konstruktør derfor erklæres som privat i klassen, mislykkes de objektinitialiseringer, der kræver offentlig adgang.
I følgende eksempel kan du se, hvordan du bruger en objektinitialiseringsfunktion.
I det første kodeeksempel vises klassedefinitionen for en klasse med navnet Cat. Definitionen omfatter to konstruktører, hvoraf den ene er en konstruktør uden parametre. Det andet kodeeksempel viser, hvordan du instantierer et Cat objekt ved hjælp af en objektinitialisering. Objektinitialiseringsfunktionen tildeler værdier til Age og Name egenskaber for det Cat objekt.
public class Cat
{
// Automatically implemented properties.
public int Age { get; set; }
public string? Name { get; set; }
public Cat()
{
}
public Cat(string name)
{
this.Name = name;
}
}
public class Program
{
public static void Main()
{
// Declare and instantiate a new Cat object by using an object initializer.
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
// Declare and instantiate a new Cat object by using an object initializer.
Cat sameCat = new Cat("Fluffy") { Age = 10 };
}
}
Her er et andet eksempel, der viser, hvordan du initialiserer en ny StudentName type ved hjælp af objektinitialiseringsfunktionen. I dette eksempel angives egenskaber i StudentName type:
public class HowToObjectInitializers
{
public static void Main()
{
// Declare a StudentName by using the constructor that has two parameters.
StudentName student1 = new StudentName("Lisa", "Yeh");
// Make the same declaration by using an object initializer and sending
// arguments for the first and last names. The parameterless constructor is
// invoked in processing this declaration, not the constructor that has
// two parameters.
StudentName student2 = new StudentName
{
FirstName = "Sandy",
LastName = "Zoeng"
};
// Declare a StudentName by using an object initializer and sending
// an argument for only the ID property. No corresponding constructor is
// necessary. Only the parameterless constructor is used to process object
// initializers.
StudentName student3 = new StudentName
{
ID = 183
};
// Declare a StudentName by using an object initializer and sending
// arguments for all three properties. No corresponding constructor is
// defined in the class.
StudentName student4 = new StudentName
{
FirstName = "Thomas",
LastName = "Margand",
ID = 116
};
Console.WriteLine(student1.ToString());
Console.WriteLine(student2.ToString());
Console.WriteLine(student3.ToString());
Console.WriteLine(student4.ToString());
}
// Output:
// Lisa 0
// Sandy 0
// 183
// Thomas 116
public class StudentName
{
// This constructor has no parameters. The parameterless constructor
// is invoked in the processing of object initializers.
// You can test this by changing the access modifier from public to
// private. The declarations in Main that use object initializers will
// fail.
public StudentName() { }
// The following constructor has parameters for two of the three
// properties.
public StudentName(string first, string last)
{
FirstName = first;
LastName = last;
}
// Properties.
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int ID { get; set; }
// Override the ToString method of the Object class.
public override string ToString() => FirstName + " " + ID;
}
}
Syntaksen for initialisering af objektet giver dig mulighed for at oprette en forekomst, og derefter tildeles det nyoprettede objekt med dets tildelte egenskaber til variablen i tildelingen.
Ud over at tildele felter og egenskaber kan objektinitialisering indstille indeksering. Overvej denne grundlæggende Matrix klasse:
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; }
}
}
Du kan initialisere identitetsmatrixen med følgende kode:
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,
};
I følgende eksempel defineres en BaseballTeam klasse, der bruger et indekseringsprogram til at hente og indstille spillere på forskellige positioner. Initialiseringsfunktionen kan tildele spillere baseret på forkortelsen for positionen eller det tal, der bruges til hver position på et baseball scorecard:
public class HowToIndexInitializer
{
public class BaseballTeam
{
private string[] players = new string[9];
private readonly List<string> positionAbbreviations = new List<string>
{
"P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"
};
public string this[int position]
{
// Baseball positions are 1 - 9.
get { return players[position-1]; }
set { players[position-1] = value; }
}
public string this[string position]
{
get { return players[positionAbbreviations.IndexOf(position)]; }
set { players[positionAbbreviations.IndexOf(position)] = value; }
}
}
public static void Main()
{
var team = new BaseballTeam
{
["RF"] = "Lisa Yeh",
[4] = "Sandy Zoeng",
["CF"] = "Thomas Margand"
};
Console.WriteLine(team["2B"]);
}
}
Objektinitialisering med den påkrævede modifikator
Du bruger det påkrævede nøgleord til at tvinge kaldere til at angive værdien for en egenskab eller et felt ved hjælp af en objektinitialiseringsfunktion. Påkrævede egenskaber behøver ikke at være angivet som konstruktørparametre. Compileren sikrer, at alle kaldere initialiserer disse værdier.
// The `FirstName` property is optional and has a default value of an empty string.
// The `LastName` property is required and must be initialized during object creation.
// You can create a new instance of the Person class using both properties.
var friend1 = new Person() { FirstName = "Lisa", LastName = "Yeh" };
Console.WriteLine($"Hello, {friend1.FirstName} {friend1.LastName}!");
// You can create a new instance of the Person class using only the LastName property.
var friend2 = new Person() { LastName = "Yeh"};
Console.WriteLine($"Hello, {friend2.FirstName} {friend2.LastName}!");
// You can assign a different value to the properties after the object is created.
friend2.FirstName = "Sandy";
friend2.LastName = "Chen";
Console.WriteLine($"Hello, {friend2.FirstName} {friend2.LastName}!");
// Compiler error: Required property 'LastName' must be initialized.
//var friend3 = new Person() { FirstName = "Lisa"};
//var friend4 = new Person();
// Output:
// Hello, Lisa Yeh!
// Hello, Yeh!
// Hello, Sandy Chen!
public class Person
{
public string FirstName { get; set; } = string.Empty;
public required string LastName { get; set; }
}
Det er en typisk praksis at sikre, at dit objekt initialiseres korrekt, især når du har flere felter eller egenskaber at administrere og ikke vil medtage dem alle i konstruktøren.
Objektinitialisering med init-accessoren
Sørg for, at ingen ændrer det designede objekt, ved hjælp af en init accessor. Det hjælper med at begrænse indstillingen af egenskabsværdien.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; init; }
}
// You can create a new instance of the Person class any combination of the properties.
var friend0 = new Person();
var friend1 = new Person() { FirstName = "Lisa" };
var friend2 = new Person() { LastName = "Yeh" };
var friend3 = new Person() { FirstName = "Lisa", LastName = "Yeh" };
Console.WriteLine($"Hello, {friend0.FirstName} {friend0.LastName}!");
Console.WriteLine($"Hello, {friend1.FirstName} {friend1.LastName}!");
Console.WriteLine($"Hello, {friend2.FirstName} {friend2.LastName}!");
Console.WriteLine($"Hello, {friend3.FirstName} {friend3.LastName}!");
// You can assign a different value to the FirstName property after the object is created, but not the LastName property.
friend3.FirstName = "Sandy";
// 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.
//friend3.LastName = "Chen";
Console.WriteLine($"Hello, {friend3.FirstName} {friend3.LastName}!");
// Output:
// Hello, unknown!
// Hello, Lisa unknown!
// Hello, Yeh!
// Hello, Lisa Yeh!
// Hello, Sandy Yeh!
Obligatoriske egenskaber kun init-understøtter uforanderlige strukturer, samtidig med at der tillades naturlig syntaks for brugere af typen .
Objektinitialiseringsfunktionen med klassespecifikke egenskaber
Det er vigtigt at overveje konsekvenserne for klassespecifikke egenskaber, når du initialiserer et objekt:
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
}
I det næste eksempel vises rækkefølgen af udførelse af konstruktør- og medlemsinitialiseringer ved hjælp af konstruktører med og uden parametre:
public class ObjectInitializersExecutionOrder
{
public static void Main()
{
new Person { FirstName = "Lisa", LastName = "Yeh", City = "unknown" };
new Dog(2) { Name = "Oscar" };
}
public class Dog
{
private int age;
private string name;
public Dog(int age)
{
Console.WriteLine("Hello from Dog's non-parameterless constructor");
this.age = age;
}
public required string Name
{
get { return name; }
set
{
Console.WriteLine("Hello from setter of Dog's required property 'Name'");
name = value;
}
}
}
public class Person
{
private string firstName;
private string lastName;
private string city;
public Person()
{
Console.WriteLine("Hello from Person's parameterless constructor");
}
public required string FirstName
{
get { return firstName; }
set
{
Console.WriteLine("Hello from setter of Person's required property 'FirstName'");
firstName = value;
}
}
public string LastName
{
get { return lastName; }
init
{
Console.WriteLine("Hello from setter of Person's init property 'LastName'");
lastName = value;
}
}
public string City
{
get { return city; }
set
{
Console.WriteLine("Hello from setter of Person's property 'City'");
city = value;
}
}
}
// Output:
// Hello from Person's parameterless constructor
// Hello from setter of Person's required property 'FirstName'
// Hello from setter of Person's init property 'LastName'
// Hello from setter of Person's property 'City'
// Hello from Dog's non-parameterless constructor
// Hello from setter of Dog's required property 'Name'
}
Kopiér konstruktører
I følgende eksempel definerer klassen Person en kopikonstruktør, der som argument bruger en forekomst af Person. Værdierne for egenskaberne for argumentet tildeles egenskaberne for den nye forekomst af Person. Koden indeholder en alternativ kopikonstruktør, der sender Name og Age egenskaber for den forekomst, du vil kopiere til klassens forekomstkonstruktør. Klassen Person er forseglet, så der kan ikke erklæres nogen afledte typer, der kan medføre fejl, ved kun at kopiere basisklassen.
public sealed class Person
{
// Copy constructor.
public Person(Person previousPerson)
{
Name = previousPerson.Name;
Age = previousPerson.Age;
}
//// Alternate copy constructor calls the instance constructor.
//public Person(Person previousPerson)
// : this(previousPerson.Name, previousPerson.Age)
//{
//}
// Instance constructor.
public Person(string name, int age)
{
Name = name;
Age = age;
}
public int Age { get; set; }
public string Name { get; set; }
public string Details()
{
return Name + " is " + Age.ToString();
}
}
class TestPerson
{
static void Main()
{
// Create a Person object by using the instance constructor.
Person person1 = new Person("Lisa", 40);
// Create another Person object, copying person1.
Person person2 = new Person(person1);
// Change each person's age.
person1.Age = 39;
person2.Age = 41;
// Change person2's name.
person2.Name = "Sandy";
// Show details to verify that the name and age fields are distinct.
Console.WriteLine(person1.Details());
Console.WriteLine(person2.Details());
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
// Output:
// Lisa is 39
// Sandy is 41