Sistema de tipo comum
O sistema de tipos comuns define como os tipos são declarados, usados e gerenciados no Common Language Runtime, e também é uma parte importante do suporte do tempo de execução para integração entre idiomas. O sistema de tipo comum executa as seguintes funções:
Estabelece uma estrutura que ajuda a permitir a integração entre idiomas, a segurança de tipo e a execução de código de alto desempenho.
Fornece um modelo orientado a objeto que suporta a implementação completa de muitas linguagens de programação.
Define regras que os idiomas devem seguir, o que ajuda a garantir que objetos escritos em idiomas diferentes possam interagir uns com os outros.
Fornece uma biblioteca que contém os tipos de dados primitivos (como Boolean, Byte, Char, Int32e UInt64) usados no desenvolvimento de aplicativos.
Tipos em .NET
Todos os tipos no .NET são tipos de valor ou tipos de referência.
Tipos de valor são tipos de dados cujos objetos são representados pelo valor real do objeto. Se uma instância de um tipo de valor for atribuída a uma variável, essa variável receberá uma nova cópia do valor.
Os tipos de referência são tipos de dados cujos objetos são representados por uma referência (semelhante a um ponteiro) ao valor real do objeto. Se um tipo de referência for atribuído a uma variável, essa variável fará referência (aponta para) o valor original. Nenhuma cópia é feita.
O sistema de tipo comum no .NET suporta as seguintes cinco categorias de tipos:
Classes
Uma classe é um tipo de referência que pode ser derivado diretamente de outra classe e que é derivado implicitamente de System.Object. A classe define as operações que um objeto (que é uma instância da classe) pode executar (métodos, eventos ou propriedades) e os dados que o objeto contém (campos). Embora uma classe geralmente inclua definição e implementação (ao contrário das interfaces, por exemplo, que contêm apenas definição sem implementação), ela pode ter um ou mais membros que não têm implementação.
A tabela a seguir descreve algumas das características que uma classe pode ter. Cada linguagem que suporta o tempo de execução fornece uma maneira de indicar que uma classe ou membro da classe tem uma ou mais dessas características. No entanto, linguagens de programação individuais que visam o .NET podem não disponibilizar todas essas características.
Characteristic | Description |
---|---|
selado | Especifica que outra classe não pode ser derivada desse tipo. |
implementa | Indica que a classe usa uma ou mais interfaces fornecendo implementações de membros da interface. |
RESUMO | Indica que a classe não pode ser instanciada. Para usá-lo, você deve derivar outra classe dele. |
herda | Indica que as instâncias da classe podem ser usadas em qualquer lugar onde a classe base seja especificada. Uma classe derivada que herda de uma classe base pode usar a implementação de quaisquer membros públicos fornecidos pela classe base, ou a classe derivada pode substituir a implementação dos membros públicos com sua própria implementação. |
exportados ou não exportados | Indica se uma classe é visível fora do assembly no qual ela é definida. Esta característica aplica-se apenas a classes de nível superior e não a classes aninhadas. |
Nota
Uma classe também pode ser aninhada em uma classe ou estrutura pai. As classes aninhadas também têm características de membro. Para obter mais informações, consulte Tipos aninhados.
Os membros da classe que não têm implementação são membros abstratos. Uma classe que tem um ou mais membros abstratos é ela própria abstrata; novas instâncias dele não podem ser criadas. Algumas linguagens direcionadas ao tempo de execução permitem marcar uma classe como abstrata, mesmo que nenhum de seus membros seja abstrato. Você pode usar uma classe abstrata quando quiser encapsular um conjunto básico de funcionalidades que as classes derivadas podem herdar ou substituir quando apropriado. As classes que não são abstratas são referidas como classes concretas.
Uma classe pode implementar qualquer número de interfaces, mas pode herdar de apenas uma classe base, além System.Objectde , da qual todas as classes herdam implicitamente. Todas as classes devem ter pelo menos um construtor, que inicializa novas instâncias da classe. Se você não definir explicitamente um construtor, a maioria dos compiladores fornecerá automaticamente um construtor sem parâmetros.
Estruturas
Uma estrutura é um tipo de valor que deriva implicitamente de System.ValueType, que por sua vez é derivado de System.Object. Uma estrutura é útil para representar valores cujos requisitos de memória são pequenos e para passar valores como parâmetros por valor para métodos que têm parâmetros fortemente tipados. No .NET, todos os tipos de dados primitivos (Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, UInt16SingleUInt32Int64SBytee UInt64) são definidos como estruturas.
Como as classes, as estruturas definem os dados (os campos da estrutura) e as operações que podem ser executadas nesses dados (os métodos da estrutura). Isso significa que você pode chamar métodos em estruturas, incluindo os métodos virtuais definidos nas System.Object classes e System.ValueType e quaisquer métodos definidos no próprio tipo de valor. Em outras palavras, as estruturas podem ter campos, propriedades e eventos, bem como métodos estáticos e não estáticos. Você pode criar instâncias de estruturas, passá-las como parâmetros, armazená-las como variáveis locais ou armazená-las em um campo de outro tipo de valor ou tipo de referência. As estruturas também podem implementar interfaces.
Os tipos de valor também diferem das classes em vários aspetos. Primeiro, embora herdem implicitamente de System.ValueType, eles não podem herdar diretamente de qualquer tipo. Da mesma forma, todos os tipos de valor são selados, o que significa que nenhum outro tipo pode ser derivado deles. Eles também não exigem construtores.
Para cada tipo de valor, o common language runtime fornece um tipo in a box correspondente, que é uma classe que tem o mesmo estado e comportamento que o tipo de valor. Uma instância de um tipo de valor é encaixotada quando é passada para um método que aceita um parâmetro do tipo System.Object. Ele é desencaixotado (ou seja, convertido de uma instância de uma classe de volta para uma instância de um tipo de valor) quando o controle retorna de uma chamada de método que aceita um tipo de valor como um parâmetro por referência. Alguns idiomas exigem que você use sintaxe especial quando o tipo em caixa é necessário; outros usam automaticamente o tipo de caixa quando é necessário. Ao definir um tipo de valor, você está definindo o tipo in a box e o unboxed.
Enumerações
Uma enumeração é um tipo de valor que herda diretamente de System.Enum e que fornece nomes alternativos para os valores de um tipo primitivo subjacente. Um tipo de enumeração tem um nome, um tipo subjacente que deve ser um dos tipos inteiros internos assinados ou não assinados (como Byte, Int32, ou UInt64) e um conjunto de campos. Os campos são campos literais estáticos, cada um dos quais representa uma constante. O mesmo valor pode ser atribuído a vários campos. Quando isso ocorre, você deve marcar um dos valores como o valor de enumeração primário para reflexão e conversão de cadeia de caracteres.
Você pode atribuir um valor do tipo subjacente a uma enumeração e vice-versa (nenhuma conversão é necessária pelo tempo de execução). Você pode criar uma instância de uma enumeração e chamar os métodos de System.Enum, bem como quaisquer métodos definidos no tipo subjacente da enumeração. No entanto, alguns idiomas podem não permitir que você passe uma enumeração como um parâmetro quando uma instância do tipo subjacente é necessária (ou vice-versa).
As seguintes restrições adicionais aplicam-se às enumerações:
Não podem definir os seus próprios métodos.
Eles não podem implementar interfaces.
Eles não podem definir propriedades ou eventos.
Eles não podem ser genéricos, a menos que sejam genéricos apenas porque estão aninhados dentro de um tipo genérico. Ou seja, uma enumeração não pode ter parâmetros de tipo próprios.
Nota
Os tipos aninhados (incluindo enumerações) criados com Visual Basic, C# e C++ incluem os parâmetros de tipo de todos os tipos genéricos incluídos e, portanto, são genéricos, mesmo que não tenham parâmetros de tipo próprios. Para obter mais informações, consulte "Tipos aninhados" no tópico de Type.MakeGenericType referência.
O FlagsAttribute atributo denota um tipo especial de enumeração chamado campo bit. O tempo de execução em si não distingue entre enumerações tradicionais e campos de bits, mas sua linguagem pode fazer isso. Quando essa distinção é feita, os operadores bit a bit podem ser usados em campos de bits, mas não em enumerações, para gerar valores sem nome. As enumerações geralmente são usadas para listas de elementos exclusivos, como dias da semana, nomes de países ou regiões e assim por diante. Os campos de bits são geralmente usados para listas de qualidades ou quantidades que podem ocorrer em combinação, como Red And Big And Fast
.
O exemplo a seguir mostra como usar campos de bits e enumerações tradicionais.
using System;
using System.Collections.Generic;
// A traditional enumeration of some root vegetables.
public enum SomeRootVegetables
{
HorseRadish,
Radish,
Turnip
}
// A bit field or flag enumeration of harvesting seasons.
[Flags]
public enum Seasons
{
None = 0,
Summer = 1,
Autumn = 2,
Winter = 4,
Spring = 8,
All = Summer | Autumn | Winter | Spring
}
public class Example
{
public static void Main()
{
// Hash table of when vegetables are available.
Dictionary<SomeRootVegetables, Seasons> AvailableIn = new Dictionary<SomeRootVegetables, Seasons>();
AvailableIn[SomeRootVegetables.HorseRadish] = Seasons.All;
AvailableIn[SomeRootVegetables.Radish] = Seasons.Spring;
AvailableIn[SomeRootVegetables.Turnip] = Seasons.Spring |
Seasons.Autumn;
// Array of the seasons, using the enumeration.
Seasons[] theSeasons = new Seasons[] { Seasons.Summer, Seasons.Autumn,
Seasons.Winter, Seasons.Spring };
// Print information of what vegetables are available each season.
foreach (Seasons season in theSeasons)
{
Console.Write(String.Format(
"The following root vegetables are harvested in {0}:\n",
season.ToString("G")));
foreach (KeyValuePair<SomeRootVegetables, Seasons> item in AvailableIn)
{
// A bitwise comparison.
if (((Seasons)item.Value & season) > 0)
Console.Write(String.Format(" {0:G}\n",
(SomeRootVegetables)item.Key));
}
}
}
}
// The example displays the following output:
// The following root vegetables are harvested in Summer:
// HorseRadish
// The following root vegetables are harvested in Autumn:
// Turnip
// HorseRadish
// The following root vegetables are harvested in Winter:
// HorseRadish
// The following root vegetables are harvested in Spring:
// Turnip
// Radish
// HorseRadish
Imports System.Collections.Generic
' A traditional enumeration of some root vegetables.
Public Enum SomeRootVegetables
HorseRadish
Radish
Turnip
End Enum
' A bit field or flag enumeration of harvesting seasons.
<Flags()> Public Enum Seasons
None = 0
Summer = 1
Autumn = 2
Winter = 4
Spring = 8
All = Summer Or Autumn Or Winter Or Spring
End Enum
' Entry point.
Public Class Example
Public Shared Sub Main()
' Hash table of when vegetables are available.
Dim AvailableIn As New Dictionary(Of SomeRootVegetables, Seasons)()
AvailableIn(SomeRootVegetables.HorseRadish) = Seasons.All
AvailableIn(SomeRootVegetables.Radish) = Seasons.Spring
AvailableIn(SomeRootVegetables.Turnip) = Seasons.Spring Or _
Seasons.Autumn
' Array of the seasons, using the enumeration.
Dim theSeasons() As Seasons = {Seasons.Summer, Seasons.Autumn, _
Seasons.Winter, Seasons.Spring}
' Print information of what vegetables are available each season.
For Each season As Seasons In theSeasons
Console.WriteLine(String.Format( _
"The following root vegetables are harvested in {0}:", _
season.ToString("G")))
For Each item As KeyValuePair(Of SomeRootVegetables, Seasons) In AvailableIn
' A bitwise comparison.
If (CType(item.Value, Seasons) And season) > 0 Then
Console.WriteLine(" " + _
CType(item.Key, SomeRootVegetables).ToString("G"))
End If
Next
Next
End Sub
End Class
' The example displays the following output:
' The following root vegetables are harvested in Summer:
' HorseRadish
' The following root vegetables are harvested in Autumn:
' Turnip
' HorseRadish
' The following root vegetables are harvested in Winter:
' HorseRadish
' The following root vegetables are harvested in Spring:
' Turnip
' Radish
' HorseRadish
Interfaces
Uma interface define um contrato que especifica uma relação "pode fazer" ou uma relação "tem uma". As interfaces são frequentemente usadas para implementar funcionalidades, como comparar e classificar (as interfaces eIComparable<T>), testar a IComparable igualdade (a IEquatable<T> interface) ou enumerar itens em uma coleção (as IEnumerable interfaces eIEnumerable<T>). As interfaces podem ter propriedades, métodos e eventos, todos os quais são membros abstratos; Ou seja, embora a interface defina os membros e suas assinaturas, ela deixa para o tipo que implementa a interface definir a funcionalidade de cada membro da interface. Isso significa que qualquer classe ou estrutura que implementa uma interface deve fornecer definições para os membros abstratos declarados na interface. Uma interface pode exigir qualquer classe ou estrutura de implementação para também implementar uma ou mais outras interfaces.
As seguintes restrições aplicam-se às interfaces:
- Uma interface pode ser declarada com qualquer acessibilidade, mas todos os membros da interface devem ter acessibilidade pública.
- As interfaces não podem definir construtores.
- As interfaces não podem definir campos.
- As interfaces podem definir apenas membros da instância. Eles não podem definir membros estáticos.
Cada idioma deve fornecer regras para mapear uma implementação para a interface que requer o membro, porque mais de uma interface pode declarar um membro com a mesma assinatura, e esses membros podem ter implementações separadas.
Delegados
Os delegados são tipos de referência que servem a um propósito semelhante ao dos ponteiros de função em C++. Eles são usados para manipuladores de eventos e funções de retorno de chamada no .NET. Ao contrário dos ponteiros de função, os delegados são seguros, verificáveis e tipo seguro. Um tipo de delegado pode representar qualquer método de instância ou método estático que tenha uma assinatura compatível.
Um parâmetro de um delegado é compatível com o parâmetro correspondente de um método se o tipo do parâmetro delegate for mais restritivo do que o tipo do parâmetro method, porque isso garante que um argumento passado para o delegado possa ser passado com segurança para o método.
Da mesma forma, o tipo de retorno de um delegado é compatível com o tipo de retorno de um método se o tipo de retorno do método for mais restritivo do que o tipo de retorno do delegado, porque isso garante que o valor de retorno do método possa ser convertido com segurança para o tipo de retorno do delegado.
Por exemplo, um delegado que tem um parâmetro de tipo IEnumerable e um tipo de retorno de Object pode representar um método que tem um parâmetro de tipo Object e um valor de retorno de tipo IEnumerable. Para obter mais informações e código de exemplo, consulte Delegate.CreateDelegate(Type, Object, MethodInfo).
Diz-se que um delegado está vinculado ao método que representa. Além de estar vinculado ao método, um delegado pode ser vinculado a um objeto. O objeto representa o primeiro parâmetro do método e é passado para o método toda vez que o delegado é chamado. Se o método for um método de instância, o objeto acoplado será passado como o parâmetro implícito this
(Me
no Visual Basic); se o método for estático, o objeto será passado como o primeiro parâmetro formal do método, e a assinatura do delegado deverá corresponder aos parâmetros restantes. Para obter mais informações e código de exemplo, consulte System.Delegate.
Todos os delegados herdam de System.MulticastDelegate, que herda de System.Delegate. As linguagens C#, Visual Basic e C++ não permitem herança desses tipos. Em vez disso, eles fornecem palavras-chave para declarar delegados.
Como os delegados herdam do MulticastDelegate, um delegado tem uma lista de invocação, que é uma lista de métodos que o delegado representa e que são executados quando o delegado é invocado. Todos os métodos na lista recebem os argumentos fornecidos quando o delegado é invocado.
Nota
O valor de retorno não é definido para um delegado que tenha mais de um método em sua lista de invocação, mesmo que o delegado tenha um tipo de retorno.
Em muitos casos, como com métodos de retorno de chamada, um delegado representa apenas um método, e as únicas ações que você precisa tomar são criar o delegado e invocá-lo.
Para delegados que representam vários métodos, o Delegate .NET fornece métodos das classes e MulticastDelegate delegar para dar suporte a operações como adicionar um método à lista de invocação de um delegado (o Delegate.Combine método), remover um método (o Delegate.Remove método) e obter a lista de invocação (o Delegate.GetInvocationList método).
Nota
Não é necessário usar esses métodos para delegados do manipulador de eventos em C#, C++ e Visual Basic, porque essas linguagens fornecem sintaxe para adicionar e remover manipuladores de eventos.
Definições de tipo
Uma definição de tipo inclui o seguinte:
- Quaisquer atributos definidos no tipo.
- A acessibilidade do tipo (visibilidade).
- O nome do tipo.
- O tipo base do tipo.
- Quaisquer interfaces implementadas pelo tipo.
- Definições para cada um dos membros do tipo.
Atributos
Os atributos fornecem metadados adicionais definidos pelo usuário. Mais comumente, eles são usados para armazenar informações adicionais sobre um tipo em seu assembly ou para modificar o comportamento de um membro do tipo no ambiente de tempo de design ou tempo de execução.
Os atributos são classes que herdam do System.Attribute. Linguagens que suportam o uso de atributos cada uma tem sua própria sintaxe para aplicar atributos a um elemento de linguagem. Os atributos podem ser aplicados a praticamente qualquer elemento de linguagem; Os elementos específicos aos quais um atributo pode ser aplicado são definidos pelo AttributeUsageAttribute que é aplicado a essa classe de atributo.
Acessibilidade do tipo
Todos os tipos têm um modificador que rege sua acessibilidade de outros tipos. A tabela a seguir descreve as acessibilidades de tipo suportadas pelo tempo de execução.
Acessibilidade | Description |
---|---|
public | O tipo é acessível por todos os assemblies. |
assemblagem | O tipo é acessível apenas a partir de sua montagem. |
A acessibilidade de um tipo aninhado depende de seu domínio de acessibilidade, que é determinado pela acessibilidade declarada do membro e pelo domínio de acessibilidade do tipo imediatamente contido. No entanto, o domínio de acessibilidade de um tipo aninhado não pode exceder o do tipo que contém.
O domínio de acessibilidade de um membro M
aninhado declarado em um tipo T
dentro de um programa P
é definido da seguinte forma (observando que M
pode ser um tipo):
Se a acessibilidade declarada de é
public
, o domínio deM
acessibilidade deM
é o domínio de acessibilidade deT
.Se a acessibilidade declarada de
M
éprotected internal
, o domínio de acessibilidade deM
é a interseção do domínio de acessibilidade deT
com o texto do programa deP
e o texto do programa de qualquer tipo derivado de foraP
declaradoT
.Se a acessibilidade declarada de
M
éprotected
, o domínio de acessibilidade deM
é a interseção do domínio de acessibilidade deT
com o texto do programa deT
e qualquer tipo derivado deT
.Se a acessibilidade declarada de
M
éinternal
, o domínio de acessibilidade deM
é a interseção do domínio de acessibilidade deT
com o texto do programa deP
.Se a acessibilidade declarada de é
private
, o domínio deM
acessibilidade de é o texto doM
programa deT
.
Nomes de tipo
O sistema de tipo comum impõe apenas duas restrições aos nomes:
- Todos os nomes são codificados como cadeias de caracteres Unicode (16 bits).
- Os nomes não podem ter um valor incorporado (16 bits) de 0x0000.
No entanto, a maioria das línguas impõe restrições adicionais aos nomes dos tipos. Todas as comparações são feitas byte a byte e, portanto, diferenciam maiúsculas de minúsculas e são independentes da localidade.
Embora um tipo possa fazer referência a tipos de outros módulos e assemblies, um tipo deve ser totalmente definido dentro de um módulo .NET. (Dependendo do suporte ao compilador, no entanto, ele pode ser dividido em vários arquivos de código-fonte.) Os nomes de tipo precisam ser exclusivos apenas dentro de um namespace. Para identificar completamente um tipo, o nome do tipo deve ser qualificado pelo namespace que contém a implementação do tipo.
Tipos de base e interfaces
Um tipo pode herdar valores e comportamentos de outro tipo. O sistema de tipos comum não permite que os tipos herdem de mais de um tipo base.
Um tipo pode implementar qualquer número de interfaces. Para implementar uma interface, um tipo deve implementar todos os membros virtuais dessa interface. Um método virtual pode ser implementado por um tipo derivado e pode ser invocado estática ou dinamicamente.
Membros do tipo
O tempo de execução permite que você defina membros do seu tipo, que especifica o comportamento e o estado de um tipo. Os membros do tipo incluem o seguinte:
Campos
Um campo descreve e contém parte do estado do tipo. Os campos podem ser de qualquer tipo suportado pelo tempo de execução. Mais comumente, os campos são ou private
protected
, de modo que eles são acessíveis apenas de dentro da classe ou de uma classe derivada. Se o valor de um campo puder ser modificado de fora de seu tipo, um acessador de conjunto de propriedades normalmente é usado. Os campos expostos publicamente são geralmente somente leitura e podem ser de dois tipos:
- Constantes, cujo valor é atribuído em tempo de design. Estes são membros estáticos de uma classe, embora eles não são definidos usando a
static
palavra-chave (Shared
no Visual Basic). - Variáveis somente leitura, cujos valores podem ser atribuídos no construtor de classe.
O exemplo a seguir ilustra esses dois usos de campos somente leitura.
using System;
public class Constants
{
public const double Pi = 3.1416;
public readonly DateTime BirthDate;
public Constants(DateTime birthDate)
{
this.BirthDate = birthDate;
}
}
public class Example
{
public static void Main()
{
Constants con = new Constants(new DateTime(1974, 8, 18));
Console.Write(Constants.Pi + "\n");
Console.Write(con.BirthDate.ToString("d") + "\n");
}
}
// The example displays the following output if run on a system whose current
// culture is en-US:
// 3.1416
// 8/18/1974
Public Class Constants
Public Const Pi As Double = 3.1416
Public ReadOnly BirthDate As Date
Public Sub New(birthDate As Date)
Me.BirthDate = birthDate
End Sub
End Class
Public Module Example
Public Sub Main()
Dim con As New Constants(#8/18/1974#)
Console.WriteLine(Constants.Pi.ToString())
Console.WriteLine(con.BirthDate.ToString("d"))
End Sub
End Module
' The example displays the following output if run on a system whose current
' culture is en-US:
' 3.1416
' 8/18/1974
Propriedades
Uma propriedade nomeia um valor ou estado do tipo e define métodos para obter ou definir o valor da propriedade. As propriedades podem ser tipos primitivos, coleções de tipos primitivos, tipos definidos pelo usuário ou coleções de tipos definidos pelo usuário. As propriedades são frequentemente usadas para manter a interface pública de um tipo independente da representação real do tipo. Isso permite que as propriedades reflitam valores que não são armazenados diretamente na classe (por exemplo, quando uma propriedade retorna um valor calculado) ou executem a validação antes que os valores sejam atribuídos a campos privados. O exemplo a seguir ilustra o último padrão.
using System;
public class Person
{
private int m_Age;
public int Age
{
get { return m_Age; }
set {
if (value < 0 || value > 125)
{
throw new ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.");
}
else
{
m_Age = value;
}
}
}
}
Public Class Person
Private m_Age As Integer
Public Property Age As Integer
Get
Return m_Age
End Get
Set
If value < 0 Or value > 125 Then
Throw New ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.")
Else
m_Age = value
End If
End Set
End Property
End Class
Além de incluir a propriedade em si, a linguagem intermediária comum (CIL) para um tipo que contém uma propriedade legível inclui um get_
método propertyname e a CIL para um tipo que contém uma propriedade gravável inclui um set_
método propertyname .
Métodos
Um método descreve as operações que estão disponíveis no tipo. A assinatura de um método especifica os tipos permitidos de todos os seus parâmetros e de seu valor de retorno.
Embora a maioria dos métodos defina o número preciso de parâmetros necessários para chamadas de método, alguns métodos suportam um número variável de parâmetros. O parâmetro final declarado desses métodos é marcado com o ParamArrayAttribute atributo. Os compiladores de linguagem normalmente fornecem uma palavra-chave, como params
em C# e ParamArray
no Visual Basic, que faz uso explícito de ParamArrayAttribute desnecessário.
Construtores
Um construtor é um tipo especial de método que cria novas instâncias de uma classe ou estrutura. Como qualquer outro método, um construtor pode incluir parâmetros; no entanto, os construtores não têm valor de retorno (ou seja, eles retornam void
).
Se o código-fonte de uma classe não define explicitamente um construtor, o compilador inclui um construtor sem parâmetros. No entanto, se o código-fonte de uma classe define apenas construtores parametrizados, os compiladores Visual Basic e C# não geram um construtor sem parâmetros.
Se o código-fonte de uma estrutura define construtores, eles devem ser parametrizados; Uma estrutura não pode definir um construtor sem parâmetros, e os compiladores não geram construtores sem parâmetros para estruturas ou outros tipos de valor. Todos os tipos de valor têm um construtor implícito sem parâmetros. Este construtor é implementado pelo common language runtime e inicializa todos os campos da estrutura para seus valores padrão.
evento
Um evento define um incidente que pode ser respondido e define métodos para assinar, cancelar a assinatura e gerar o evento. Os eventos são frequentemente usados para informar outros tipos de alterações de estado. Para obter mais informações, consulte Eventos.
Tipos aninhados
Um tipo aninhado é um tipo que é um membro de algum outro tipo. Os tipos aninhados devem ser firmemente acoplados ao seu tipo de contenção e não devem ser úteis como um tipo de uso geral. Os tipos aninhados são úteis quando o tipo declarante usa e cria instâncias do tipo aninhado e o uso do tipo aninhado não é exposto em membros públicos.
Os tipos aninhados são confusos para alguns desenvolvedores e não devem ser publicamente visíveis, a menos que haja um motivo convincente para a visibilidade. Em uma biblioteca bem projetada, os desenvolvedores raramente devem usar tipos aninhados para instanciar objetos ou declarar variáveis.
Características dos membros do tipo
O sistema de tipo comum permite que os membros do tipo tenham uma variedade de características; no entanto, os idiomas não são necessários para suportar todas essas características. A tabela a seguir descreve as características dos membros.
Characteristic | Pode candidatar-se a | Description |
---|---|---|
RESUMO | Métodos, propriedades e eventos | O tipo não fornece a implementação do método. Os tipos que herdam ou implementam métodos abstratos devem fornecer uma implementação para o método. A única exceção é quando o tipo derivado é em si um tipo abstrato. Todos os métodos abstratos são virtuais. |
privado, familiar, assembleia, família e reunião, família ou assembleia, ou público | Todos | Define a acessibilidade do membro: private Acessível apenas a partir do mesmo tipo que o membro ou dentro de um tipo aninhado. família Acessível a partir do mesmo tipo que o membro e de tipos derivados que herdam dele. assemblagem Acessível apenas na montagem em que o tipo é definido. família e assembleia Acessível apenas a partir de tipos que se qualificam para acesso à família e à assembleia. família ou assembleia Acessível apenas a partir de tipos que se qualificam para acesso à família ou à assembleia. public Acessível a partir de qualquer tipo. |
Finais | Métodos, propriedades e eventos | O método virtual não pode ser substituído em um tipo derivado. |
Somente inicialização | Campos | O valor só pode ser inicializado e não pode ser escrito após a inicialização. |
instância | Campos, métodos, propriedades e eventos | Se um membro não estiver marcado como static (C# e C++), Shared (Visual Basic), virtual (C# e C++) ou Overridable (Visual Basic), ele será um membro de instância (não há palavra-chave de instância). Haverá tantas cópias de tais membros na memória quanto há objetos que a usam. |
Literal | Campos | O valor atribuído ao campo é um valor fixo, conhecido em tempo de compilação, de um tipo de valor incorporado. Os campos literais são por vezes referidos como constantes. |
newslot ou substituir | Todos | Define como o membro interage com membros herdados que têm a mesma assinatura: Noticiário Oculta membros herdados que têm a mesma assinatura. Substituir Substitui a definição de um método virtual herdado. O padrão é newslot. |
estático | Campos, métodos, propriedades e eventos | O membro pertence ao tipo em que está definido, não a uma instância específica do tipo; O membro existe mesmo que uma instância do tipo não seja criada e é compartilhada entre todas as instâncias do tipo. |
virtual | Métodos, propriedades e eventos | O método pode ser implementado por um tipo derivado e pode ser invocado estaticamente ou dinamicamente. Se a invocação dinâmica for usada, o tipo da instância que faz a chamada em tempo de execução (em vez do tipo conhecido em tempo de compilação) determinará qual implementação do método será chamada. Para invocar um método virtual estaticamente, a variável pode ter que ser convertida para um tipo que usa a versão desejada do método. |
Sobrecarga
Cada membro do tipo tem uma assinatura exclusiva. As assinaturas do método consistem no nome do método e em uma lista de parâmetros (a ordem e os tipos dos argumentos do método). Vários métodos com o mesmo nome podem ser definidos dentro de um tipo, desde que suas assinaturas sejam diferentes. Quando dois ou mais métodos com o mesmo nome são definidos, diz-se que o método está sobrecarregado. Por exemplo, no System.Char, o IsDigit método está sobrecarregado. Um método leva um Chararquivo . O outro método leva um String e um Int32.
Nota
O tipo de retorno não é considerado parte da assinatura de um método. Ou seja, os métodos não podem ser sobrecarregados se diferirem apenas pelo tipo de retorno.
Herdar, substituir e ocultar membros
Um tipo derivado herda todos os membros de seu tipo base; ou seja, esses membros são definidos e estão disponíveis para o tipo derivado. O comportamento ou qualidades dos membros herdados podem ser modificados de duas maneiras:
Um tipo derivado pode ocultar um membro herdado definindo um novo membro com a mesma assinatura. Isso pode ser feito para tornar um membro anteriormente público privado ou para definir um novo comportamento para um método herdado marcado como
sealed
.Um tipo derivado pode substituir um método virtual herdado. O método de substituição fornece uma nova definição do método que será invocado com base no tipo do valor em tempo de execução em vez do tipo da variável conhecida em tempo de compilação. Um método pode substituir um método virtual somente se o método virtual não estiver marcado como
sealed
e o novo método for pelo menos tão acessível quanto o método virtual.