Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O record modificador fornece funcionalidade incorporada para encapsular dados. A record class sintaxe e record define tipos de referência. A record struct sintaxe define um tipo de valor.
A referência da linguagem C# documenta a versão mais recentemente lançada da linguagem C#. Contém também documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento linguístico.
A documentação identifica qualquer funcionalidade introduzida pela primeira vez nas últimas três versões da língua ou em pré-visualizações públicas atuais.
Sugestão
Para saber quando uma funcionalidade foi introduzida pela primeira vez em C#, consulte o artigo sobre o histórico de versões da linguagem C#.
Quando você declara um construtor primário em um registro, o compilador gera propriedades públicas para os parâmetros do construtor primário. Os principais parâmetros construtores de um registo são parâmetros posicionais. O compilador cria propriedades posicionais que espelham o construtor primário ou parâmetros posicionais. O compilador não sintetiza propriedades para parâmetros primários do construtor em tipos que não têm o record modificador.
Os dois exemplos seguintes demonstram record (ou record class) tipos de referência:
public record Person(string FirstName, string LastName);
public record Person
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
};
Os dois exemplos a seguir demonstram record struct tipos de valor:
public readonly record struct Point(double X, double Y, double Z);
public record struct Point
{
public double X { get; init; }
public double Y { get; init; }
public double Z { get; init; }
}
Você também pode criar registros com propriedades e campos mutáveis:
public record Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
};
As estruturas de registro também podem ser mutáveis, tanto as estruturas de registro posicional quanto as estruturas de registro sem parâmetros posicionais:
public record struct DataMeasurement(DateTime TakenAt, double Measurement);
public record struct Point
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Embora os registros possam ser mutáveis, eles se destinam principalmente ao suporte a modelos de dados imutáveis. O tipo de registo oferece as seguintes características:
- Sintaxe concisa para criar um tipo de referência com propriedades imutáveis
- Comportamento interno útil para um tipo de referência centrado em dados:
- Suporte para hierarquias de herança
Os exemplos anteriores mostram algumas distinções entre registros que são tipos de referência e registros que são tipos de valor:
- A
recordou arecord classdeclara um tipo de referência. Aclasspalavra-chave é opcional, mas pode adicionar clareza para os leitores. Arecord structdeclara um tipo de valor. - As propriedades posicionais são imutáveis em a
record classe areadonly record struct. Eles são mutáveis em umrecord structarquivo .
O restante deste artigo discute ambos e record classrecord struct tipos. As diferenças são detalhadas em cada seção. Decida entre a record class e a record struct semelhante a decidir entre a class e a struct. O termo registo descreve comportamentos que se aplicam a todos os tipos de registo. Ou record structrecord class é usado para descrever o comportamento que se aplica apenas aos tipos struct ou class, respectivamente.
Sintaxe posicional para definição de propriedade e campo
Use parâmetros posicionais para declarar propriedades de um registo ou para inicializar valores de propriedades ou campos. O exemplo a seguir cria um registro com duas propriedades posicionais:
public record Person(string FirstName, string LastName);
public static void Main()
{
Person person = new("Nancy", "Davolio");
Console.WriteLine(person);
// output: Person { FirstName = Nancy, LastName = Davolio }
}
Quando você usa a sintaxe posicional para definição de propriedade, o compilador cria:
- Uma propriedade pública implementada automaticamente para cada parâmetro posicional fornecido na declaração de registro.
- Para
recordtipos ereadonly record structtipos: Uma propriedade somente init. - Para
record structtipos: Uma propriedade de leitura-gravação.
- Para
- Um construtor primário cujos parâmetros correspondem aos parâmetros posicionais na declaração de registro.
- Para tipos struct de registro, um construtor sem parâmetros que define cada campo com seu valor padrão.
- Um
Deconstructmétodo com umoutparâmetro para cada parâmetro posicional fornecido na declaração de registo. O método desconstrói propriedades definidas usando sintaxe posicional; ele ignora as propriedades que são definidas usando a sintaxe de propriedade padrão.
Talvez você queira adicionar atributos a qualquer um desses elementos que o compilador cria a partir da definição de registro. Você pode adicionar um destino a qualquer atributo aplicado às propriedades do registro posicional. O exemplo a seguir aplica o System.Text.Json.Serialization.JsonPropertyNameAttribute a cada propriedade do Person registro. O property: destino indica que o atributo é aplicado à propriedade gerada pelo compilador. Outros valores são field: aplicar o atributo ao campo e param: aplicar o atributo ao parâmetro.
/// <summary>
/// Person record type
/// </summary>
/// <param name="FirstName">First Name</param>
/// <param name="LastName">Last Name</param>
/// <remarks>
/// The person type is a positional record containing the
/// properties for the first and last name. Those properties
/// map to the JSON elements "firstName" and "lastName" when
/// serialized or deserialized.
/// </remarks>
public record Person([property: JsonPropertyName("firstName")] string FirstName,
[property: JsonPropertyName("lastName")] string LastName);
O exemplo anterior também mostra como criar comentários de documentação XML para o registro. Você pode adicionar a <param> tag para adicionar documentação para os parâmetros do construtor primário.
Se a definição de propriedade implementada automaticamente não for o que queres, define a tua própria propriedade ou campo com o mesmo nome. Por exemplo, você pode querer alterar a acessibilidade ou mutabilidade, ou fornecer uma implementação para o get ou set acessador. Caso declare o membro no seu código-fonte, deverá inicializá-lo a partir do parâmetro posicional do registo. Se sua propriedade for uma propriedade implementada automaticamente, você deverá inicializá-la. Se você adicionar um campo de suporte em sua origem, deverá inicializar o campo de suporte. O desconstrutor gerado usa sua propriedade ou definição de campo. Por exemplo, o exemplo a seguir declara as FirstName propriedades e LastName de um registro publicposicional, mas restringe o Id parâmetro posicional a internal. Você pode usar essa sintaxe para registros e tipos de estrutura de registro.
public record Person(string FirstName, string LastName, string Id)
{
internal string Id { get; init; } = Id;
}
public static void Main()
{
Person person = new("Nancy", "Davolio", "12345");
Console.WriteLine(person.FirstName); //output: Nancy
}
Se desejar criar um campo em vez de uma propriedade, atribua o parâmetro posicional a um campo, conforme mostrado no exemplo a seguir:
public record Person(string FirstName, string LastName, string Id)
{
internal readonly string Id = Id; // this.Id set to parameter Id
}
public static void Main()
{
Person person = new("Nancy", "Davolio", "12345");
Console.WriteLine(person.FirstName); //output: Nancy
}
Um tipo de registro não precisa declarar nenhuma propriedade posicional. Você pode declarar um registro sem quaisquer propriedades posicionais e pode declarar outros campos e propriedades, como no exemplo a seguir:
public record Person(string FirstName, string LastName)
{
public string[] PhoneNumbers { get; init; } = [];
};
As propriedades que o compilador gera a partir de parâmetros posicionais são public. Você declara os modificadores de acesso em todas as propriedades declaradas explicitamente.
Imutabilidade
Uma classe de registo posicional e uma estrutura de registo posicional somente leitura declaram propriedades somente de inicialização. Uma struct de registro posicional declara propriedades de leitura-gravação. Você pode substituir qualquer um desses padrões, conforme mostrado na seção anterior.
A imutabilidade pode ser útil quando precisas que um tipo centrado em dados seja seguro para threads ou quando dependes de um código de hash que permanece igual numa tabela de hash. No entanto, a imutabilidade não é adequada para todos os cenários de dados. Entity Framework Core, por exemplo, não suporta atualizações com tipos de entidades imutáveis.
Propriedades apenas de init, quer criadas a partir de parâmetros posicionais (record class e readonly record struct) ou por especificação init de acessórios, têm imutabilidade superficial. Após a inicialização, não é possível alterar o valor das propriedades de tipo de valor ou a referência de propriedades de tipo de referência. No entanto, os dados aos quais uma propriedade de tipo de referência se refere podem ser alterados. O exemplo a seguir mostra que o conteúdo de uma propriedade imutável do tipo de referência (uma matriz, neste caso) é mutável:
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static void Main()
{
Person person = new("Nancy", "Davolio", new string[1] { "555-1234" });
Console.WriteLine(person.PhoneNumbers[0]); // output: 555-1234
person.PhoneNumbers[0] = "555-6789";
Console.WriteLine(person.PhoneNumbers[0]); // output: 555-6789
}
Os recursos exclusivos para tipos de registro são implementados por métodos sintetizados pelo compilador, e nenhum desses métodos compromete a imutabilidade modificando o estado do objeto. A menos que especificado, os métodos sintetizados são gerados para record, record structe readonly record struct declarações.
Igualdade de valores
Se você não substituir ou substituir métodos de igualdade, o tipo declarado governará como a igualdade é definida:
- Para
classtipos, dois objetos são iguais se se referirem ao mesmo objeto na memória. - Para
structtipos, dois objetos são iguais se forem do mesmo tipo e armazenarem os mesmos valores. - Para tipos com o
recordmodificador (record class,record struct, ereadonly record struct), dois objetos são iguais se forem do mesmo tipo e armazenarem os mesmos valores.
A definição de igualdade para um record struct é a mesma que para um struct. A diferença é que, para um struct, a implementação está em ValueType.Equals(Object) e depende de reflexão. Para registros, a implementação é sintetizada pelo compilador e usa os membros de dados declarados.
A igualdade de referência é necessária para alguns modelos de dados. Por exemplo, Entity Framework Core depende da igualdade de referência para garantir que utiliza apenas uma instância de um tipo de entidade para o que é conceptualmente uma entidade. Por esta razão, registos e estruturas de registo não são apropriados para utilização como tipos de entidade no Entity Framework Core.
O exemplo a seguir ilustra a igualdade de valor dos tipos de registro:
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static void Main()
{
var phoneNumbers = new string[2];
Person person1 = new("Nancy", "Davolio", phoneNumbers);
Person person2 = new("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // output: True
person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // output: True
Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
}
Para implementar a igualdade de valor, o compilador sintetiza vários métodos, incluindo:
Uma substituição de Object.Equals(Object). É um erro se declarares explicitamente o override.
Este método é usado como base para o método estático quando ambos os Object.Equals(Object, Object) parâmetros são não-nulos.
A
virtual, ousealed,Equals(R? other)ondeRé o tipo de registo. Este método implementa IEquatable<T>. Pode declarar este método explicitamente.Se o tipo de registo for derivado de um tipo
Basede registo de base ,Equals(Base? other). É um erro se declarares explicitamente o override. Se você fornecer sua própria implementação deEquals(R? other), forneça uma implementação deGetHashCodetambém.Uma substituição de Object.GetHashCode(). Pode declarar este método explicitamente.
Sobreposições de operador
==e operador!=. É um erro se declarares os operadores explicitamente.Se o tipo de registo for derivado de um tipo de registo base,
protected override Type EqualityContract { get; };. Pode declarar esta propriedade explicitamente. Para obter mais informações, consulte Igualdade nas hierarquias de herança.
O compilador não sintetiza um método quando um tipo de registo tem um método que corresponde à assinatura de um método sintetizado e o método pode ser declarado explicitamente.
Mutação não destrutiva
Se precisares de copiar uma instância com algumas modificações, usa uma with expressão para obter uma mutação não destrutiva. Uma with expressão cria uma nova instância de registo que é uma cópia de uma instância de registo existente, mas com propriedades e campos especificados modificados. Use a sintaxe do inicializador de objetos para especificar os valores a alterar, como mostrado no exemplo seguinte:
public record Person(string FirstName, string LastName)
{
public string[] PhoneNumbers { get; init; }
}
public static void Main()
{
Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
Console.WriteLine(person1);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Person person2 = person1 with { FirstName = "John" };
Console.WriteLine(person2);
// output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { PhoneNumbers = new string[1] };
Console.WriteLine(person2);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { };
Console.WriteLine(person1 == person2); // output: True
}
A with expressão pode definir propriedades posicionais ou propriedades criadas usando sintaxe de propriedade padrão. As propriedades declaradas explicitamente devem ter um init acessador ou set para serem alteradas em uma with expressão.
O resultado de uma with expressão é uma cópia superficial. Para uma propriedade de referência, a expressão copia apenas a referência para uma instância. Tanto o registo original como a cópia acabam por ter uma referência ao mesmo caso.
Para implementar esse recurso para record class tipos, o compilador sintetiza um método de clone e um construtor de cópia. O método de clone virtual retorna um novo registro inicializado pelo construtor copy. Quando usas uma with expressão, o compilador cria código que chama o método clone e depois define as propriedades que a with expressão inclui.
Importante
O compilador também sintetiza um construtor público sem parâmetros quando o registo não possui um construtor primário ou quaisquer construtores definidos pelo utilizador. Este construtor sem parâmetros inicializa todos os campos aos seus valores padrão. Sem este construtor sintetizado, nenhum construtor público está disponível.
Se precisares de um comportamento de cópia diferente, escreve o teu próprio construtor de cópias num record class. Se o fizeres, o compilador não sintetiza um. Faça o seu construtor private se o registo for sealed. Caso contrário, faz com que protectedseja . O compilador não sintetiza um construtor de cópia para record struct tipos. Você pode escrever um, mas o compilador não gera chamadas para expressões with . Os valores do record struct são copiados na atribuição.
Você não pode substituir o método de clone e não pode criar um membro nomeado Clone em qualquer tipo de registro. O nome real do método clone é gerado pelo compilador.
Importante
Nos exemplos anteriores, todas as propriedades são independentes. Nenhuma das propriedades é calculada a partir de outros valores de propriedade. Uma with expressão primeiro copia a instância de registo existente, depois modifica quaisquer propriedades ou campos que a with expressão inclua. As propriedades computadas em tipos devem ser calculadas no record acesso, não inicializadas quando a instância é criada. Caso contrário, uma propriedade poderia retornar o valor calculado com base na instância original, não na cópia modificada.
Você garante a correção nas propriedades computadas calculando o valor no acesso, conforme mostrado na seguinte declaração:
public record Point(int X, int Y)
{
public double Distance => Math.Sqrt(X * X + Y * Y);
}
O tipo de registro anterior calcula o Distance quando acessado, conforme mostrado no exemplo a seguir:
Point p1 = new Point(3, 4);
Console.WriteLine($"Original point: {p1}");
p1 = p1 with { Y = 8 };
Console.WriteLine($"Modified point: {p1}");
// Output:
// Original point: Point { X = 3, Y = 4, Distance = 5 }
// Modified point: Point { X = 3, Y = 8, Distance = 8.54400374531753 }
Contraste essa abordagem com a seguinte declaração, onde a Distance propriedade é calculada e armazenada em cache como parte da inicialização de uma nova instância:
public record PointInit(int X, int Y)
{
public double Distance { get; } = Math.Sqrt(X * X + Y * Y);
}
Como Distance é calculado como parte da inicialização, o valor é calculado e armazenado em cache antes que a with expressão altere o valor de Y na cópia. O resultado é que a distância está incorreta:
PointInit pt1 = new PointInit(3, 4);
Console.WriteLine($"Original point: {pt1}");
pt1 = pt1 with { Y = 8 };
Console.WriteLine($"Incorrect Modified point: {pt1}");
// Output:
// Original point: PointInit { X = 3, Y = 4, Distance = 5 }
// Modified point: PointInit { X = 3, Y = 8, Distance = 5 }
A Distance computação não é cara para calcular em cada acesso. No entanto, algumas propriedades computadas podem exigir acesso a mais dados ou computação mais extensa. Nesses casos, em vez de um registro, use um class tipo e calcule o valor armazenado em cache quando um dos componentes alterar o valor.
Formatação integrada para exibição
Os tipos de registro têm um método gerado pelo ToString compilador que exibe os nomes e valores de propriedades e campos públicos. O ToString método devolve uma cadeia no seguinte formato:
<nome> do tipo de registro { <nome da> propriedade = <valor>, <nome da> propriedade = <valor>, ...}
A cadeia de caracteres impressa para <value> é a cadeia de caracteres retornada pelo ToString() para o tipo da propriedade. No exemplo a seguir, ChildNames é um System.Array, onde ToString retorna System.String[]:
Person { FirstName = Nancy, LastName = Davolio, ChildNames = System.String[] }
Para implementar esse recurso, em record class tipos, o compilador sintetiza um método virtual PrintMembers e uma ToString substituição. Em record struct tipos, este membro é private.
A ToString substituição cria um StringBuilder objeto com o nome do tipo seguido por um colchete de abertura. Ele chama PrintMembers para adicionar nomes de propriedade e valores e, em seguida, adiciona o colchete de fechamento. O exemplo a seguir mostra um código semelhante ao que a substituição sintetizada contém:
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Teacher"); // type name
stringBuilder.Append(" { ");
if (PrintMembers(stringBuilder))
{
stringBuilder.Append(" ");
}
stringBuilder.Append("}");
return stringBuilder.ToString();
}
Você pode fornecer sua própria implementação ou PrintMembers a ToString substituição. Exemplos são fornecidos na seção formatação em registrosPrintMembers neste artigo. Sua implementação de ToString pode incluir o modificador sealed, que impede que o compilador sintetize uma implementação de ToString para quaisquer registros derivados. Você pode criar uma representação de cadeia de caracteres consistente em toda uma hierarquia de record tipos. (Os registros derivados ainda têm um PrintMembers método gerado para todas as propriedades derivadas.)
Herança
Esta secção aplica-se apenas aos record class tipos.
Um registro pode herdar de outro registro. No entanto, um registro não pode herdar de uma classe e uma classe não pode herdar de um registro.
A partir de C# 15, a record class pode usar o closed modificador para restringir a derivação direta ao seu conjunto declarante. Os consumidores podem então escrever expressões exaustivas switch sobre os seus descendentes diretos sem um braço padrão. Para mais detalhes, veja Padrões de hierarquia fechada. O closed modificador não se aplica aos record struct tipos, porque os registos struct não podem ser abstratos.
Parâmetros de posição em tipos de registo derivados
O registro derivado declara parâmetros posicionais para todos os parâmetros no construtor primário do registro base. O registro base declara e inicializa essas propriedades. O registro derivado não os oculta, mas apenas cria e inicializa propriedades para parâmetros que não são declarados em seu registro base.
O exemplo a seguir ilustra a herança com sintaxe de propriedade posicional:
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Console.WriteLine(teacher);
// output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}
Igualdade nas hierarquias sucessórias
Esta seção se aplica a tipos, mas não record class a record struct tipos. Para que duas variáveis de registro sejam iguais, o tipo de tempo de execução deve ser igual. Os tipos das variáveis que contêm podem ser diferentes. A comparação da igualdade herdada é ilustrada no exemplo de código a seguir:
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Person student = new Student("Nancy", "Davolio", 3);
Console.WriteLine(teacher == student); // output: False
Student student2 = new Student("Nancy", "Davolio", 3);
Console.WriteLine(student2 == student); // output: True
}
No exemplo, todas as variáveis são declaradas como Person, mesmo quando a instância é um tipo derivado de um Student ou Teacher. As instâncias têm as mesmas propriedades e os mesmos valores de propriedade. Mas student == teacher retorna False embora ambas sejam Personvariáveis do tipo -, e student == student2 retorna True embora uma seja uma Person variável e uma seja uma Student variável. O teste de igualdade depende do tipo de tempo de execução do objeto real, não do tipo declarado da variável.
Para implementar esse comportamento, o compilador sintetiza uma EqualityContract propriedade que retorna um Type objeto que corresponde ao tipo do registro. O EqualityContract permite que os métodos de igualdade comparem o tipo de tempo de execução de objetos quando eles estão verificando a igualdade. Se o tipo base de um registro for object, essa propriedade será virtual. Se o tipo base for outro tipo de registro, essa propriedade será uma substituição. Se o tipo de registro for sealed, essa propriedade será efetivamente sealed porque o tipo é sealed.
Quando o código compara duas instâncias de um tipo derivado, os métodos de igualdade sintetizados verificam todos os membros de dados da base e tipos derivados para igualdade. O método sintetizado GetHashCode usa o GetHashCode método de todos os membros de dados declarados no tipo base e no tipo de registro derivado. Os membros de dados de um record incluem todos os campos declarados e o campo de suporte sintetizado pelo compilador para quaisquer propriedades implementadas automaticamente.
with expressões em registos derivados
O resultado de uma with expressão tem o mesmo tipo de tempo de execução que o operando da expressão. Todas as propriedades do tipo de tempo de execução são copiadas, mas você só pode definir propriedades do tipo de tempo de compilação, como mostra o exemplo a seguir:
public record Point(int X, int Y)
{
public int Zbase { get; set; }
};
public record NamedPoint(string Name, int X, int Y) : Point(X, Y)
{
public int Zderived { get; set; }
};
public static void Main()
{
Point p1 = new NamedPoint("A", 1, 2) { Zbase = 3, Zderived = 4 };
Point p2 = p1 with { X = 5, Y = 6, Zbase = 7 }; // Can't set Name or Zderived
Console.WriteLine(p2 is NamedPoint); // output: True
Console.WriteLine(p2);
// output: NamedPoint { X = 5, Y = 6, Zbase = 7, Name = A, Zderived = 4 }
Point p3 = (NamedPoint)p1 with { Name = "B", X = 5, Y = 6, Zbase = 7, Zderived = 8 };
Console.WriteLine(p3);
// output: NamedPoint { X = 5, Y = 6, Zbase = 7, Name = B, Zderived = 8 }
}
PrintMembers formatação em registros derivados
O método sintetizado PrintMembers de um tipo de registro derivado chama a implementação base. O resultado é que a ToString saída inclui todas as propriedades públicas e campos tanto de tipos derivados como base, como mostrado no seguinte exemplo:
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Console.WriteLine(teacher);
// output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}
Você pode fornecer sua própria implementação do PrintMembers método. Se o fizer, use a seguinte assinatura:
- Para um
sealedregistro que deriva deobject(não declara um registro base):private bool PrintMembers(StringBuilder builder); - Para um
sealedregisto que deriva de outro registo (o tipo de anexo ésealed, pelo que o método é efetivamentesealed):protected override bool PrintMembers(StringBuilder builder); - Para um registro que não
sealedé e deriva do objeto:protected virtual bool PrintMembers(StringBuilder builder); - Para um registro que não
sealedé e deriva de outro registro:protected override bool PrintMembers(StringBuilder builder);
Aqui está um exemplo de código que substitui os métodos sintetizados PrintMembers , um para um tipo de registro que deriva de objeto e outro para um tipo de registro que deriva de outro registro:
public abstract record Person(string FirstName, string LastName, string[] PhoneNumbers)
{
protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
stringBuilder.Append($"FirstName = {FirstName}, LastName = {LastName}, ");
stringBuilder.Append($"PhoneNumber1 = {PhoneNumbers[0]}, PhoneNumber2 = {PhoneNumbers[1]}");
return true;
}
}
public record Teacher(string FirstName, string LastName, string[] PhoneNumbers, int Grade)
: Person(FirstName, LastName, PhoneNumbers)
{
protected override bool PrintMembers(StringBuilder stringBuilder)
{
if (base.PrintMembers(stringBuilder))
{
stringBuilder.Append(", ");
}
;
stringBuilder.Append($"Grade = {Grade}");
return true;
}
};
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", new string[2] { "555-1234", "555-6789" }, 3);
Console.WriteLine(teacher);
// output: Teacher { FirstName = Nancy, LastName = Davolio, PhoneNumber1 = 555-1234, PhoneNumber2 = 555-6789, Grade = 3 }
}
Nota
O compilador sintetiza PrintMembers em registros derivados, mesmo quando um registro base selou o método ToString. Você também pode criar sua própria implementação do PrintMembers.
Comportamento do desconstrutor em registros derivados
O Deconstruct método de um registro derivado retorna os valores de todas as propriedades posicionais do tipo de tempo de compilação. Se o tipo de variável for um registo base, a operação de desconstrução devolve apenas as propriedades do registo base, a menos que o objeto seja lançado para o tipo derivado. O exemplo a seguir demonstra chamar um desconstrutor em um registro derivado.
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
var (firstName, lastName) = teacher; // Doesn't deconstruct Grade
Console.WriteLine($"{firstName}, {lastName}");// output: Nancy, Davolio
var (fName, lName, grade) = (Teacher)teacher;
Console.WriteLine($"{fName}, {lName}, {grade}");// output: Nancy, Davolio, 3
}
Restrições genéricas
A record palavra-chave é um modificador para um class ou struct tipo. Adicionar o record modificador inclui o comportamento descrito anteriormente neste artigo. Não há nenhuma restrição genérica que exija que um tipo seja um registro. A record class satisfaz a class restrição. A record struct satisfaz a struct restrição. Para obter mais informações, consulte Restrições em parâmetros de tipo.
Especificação da linguagem C#
Para obter mais informações, consulte a seção Classes da especificação da linguagem C#.
Para obter mais informações sobre esses recursos, consulte as seguintes notas de proposta de recurso: