10 Conversões
10.1 Geral
Uma conversão faz com que uma expressão seja convertida ou tratada como sendo de um tipo específico; no primeiro caso, uma conversão pode envolver uma mudança na representação. As conversões podem ser implícitas ou explícitas, e isso determina se uma conversão explícita é necessária.
Exemplo: Por exemplo, a conversão de tipo
int
para tipolong
é implícita, portanto, expressões de tipoint
podem ser tratadas implicitamente como tipolong
. A conversão oposta, de tipolong
para tipoint
, é explícita e, portanto, uma conversão explícita é necessária.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to int
exemplo de fim
Algumas conversões são definidas pelo idioma. Os programas também podem definir suas próprias conversões (§10.5).
Algumas conversões na linguagem são definidas de expressões para tipos, outras de tipos para tipos. Uma conversão de um tipo se aplica a todas as expressões que têm esse tipo.
Exemplo:
enum Color { Red, Blue, Green } // The expression 0 converts implicitly to enum types Color c0 = 0; // Other int expressions need explicit conversion Color c1 = (Color)1; // Conversion from null expression (no type) to string string x = null; // Conversion from lambda expression to delegate type Func<int, int> square = x => x * x;
exemplo de fim
10.2 Conversões implícitas
10.2.1 Geral
As seguintes conversões são classificadas como conversões implícitas:
- Conversões de identidade (§10.2.2)
- Conversões numéricas implícitas (§10.2.3)
- Conversões de enumeração implícita (§10.2.4)
- Conversões de cadeia de caracteres interpoladas implícitas (§10.2.5)
- Conversões de referência implícitas (§10.2.8)
- Conversões de boxing (§10.2.9)
- Conversões dinâmicas implícitas (§10.2.10)
- Conversões de parâmetro de tipo implícito (§10.2.12)
- Conversões de expressão constante implícita (§10.2.11)
- Conversões implícitas definidas pelo usuário (incluindo levantadas) (§10.2.14)
- Conversões de função anônimas (§10.2.15)
- Conversões de grupo de métodos (§10.2.15)
- Conversões literais nulas (§10.2.7)
- Conversões implícitas anuláveis (§10.2.6)
- Conversões de tupla implícitas (§10.2.13)
- Conversões literais padrão (§10.2.16)
- Conversões de lançamento implícitas (§10.2.17)
As conversões implícitas podem ocorrer em uma variedade de situações, incluindo invocações de membro de função (§12.6.6), expressões de conversão (§12.9.7) e atribuições (§12.21).
As conversões implícitas predefinidas sempre são bem-sucedidas e nunca fazem com que exceções sejam lançadas.
Observação: as conversões implícitas definidas pelo usuário projetadas corretamente também devem exibir essas características. nota final
Para fins de conversão, os tipos object
e são conversíveis em identidade (§10.2.2dynamic
).
No entanto, as conversões dinâmicas (§10.2.10) aplicam-se somente a expressões do tipo dynamic
(§8.2.4).
10.2.2 Conversão de identidade
Uma conversão de identidade converte de qualquer tipo para o mesmo tipo ou um tipo equivalente em runtime. Uma razão pela qual essa conversão existe é para que um tipo T
ou uma expressão de tipo T
possa ser considerada conversível em T
si mesma. Existem as seguintes conversões de identidade:
- Entre
T
eT
, para qualquer tipoT
. - Entre
T
eT?
para qualquer tipoT
de referência . - Entre
object
edynamic
. - Entre todos os tipos de tupla com a mesma aridade e o tipo construído
ValueTuple<...>
correspondente, quando existe uma conversão de identidade entre cada par de tipos de elementos correspondentes. - Entre tipos construídos a partir do mesmo tipo genérico em que existe uma conversão de identidade entre cada argumento de tipo correspondente.
Exemplo: O seguinte ilustra a natureza recursiva da terceira regra:
(int a , string b) t1 = (1, "two"); (int c, string d) t2 = (3, "four"); // Identity conversions exist between // the types of t1, t2, and t3. var t3 = (5, "six"); t3 = t2; t2 = t1; var t4 = (t1, 7); var t5 = (t2, 8); // Identity conversions exist between // the types of t4, t5, and t6. var t6 =((8, "eight"), 9); t6 = t5; t5 = t4;
Os tipos de tuplas
t1
,t2
et3
todos têm dois elementos: umint
seguido por umstring
. Os tipos de elementos de tupla podem ser eles próprios por tuplas, como emt4
,t5
, et6
. Existe uma conversão de identidade entre cada par de tipos de elementos correspondentes, incluindo tuplas aninhadas, portanto, existe uma conversão de identidade entre os tipos de tuplast4
,t5
, et6
.exemplo de fim
Todas as conversões de identidade são simétricas. Se existe uma conversão de identidade de T₁
para T₂
, então existe uma conversão de identidade de T₂
para T₁
. Dois tipos são conversíveis de identidade quando existe uma conversão de identidade entre dois tipos.
Na maioria dos casos, uma conversão de identidade não tem efeito em runtime. No entanto, como as operações de ponto flutuante podem ser executadas com maior precisão do que a prescrita por seu tipo (§8.3.7), a atribuição de seus resultados pode resultar em uma perda de precisão, e conversões explícitas são garantidas para reduzir a precisão ao que é prescrito pelo tipo (§12.9.7).
10.2.3 Conversões numéricas implícitas
As conversões numéricas implícitas são:
- De
sbyte
parashort
,int
,long
,float
,double
, oudecimal
. - De para , , ,
int
long
uint
, ,ulong
, ,float
, , oudecimal
double
.ushort
short
byte
- De
short
paraint
,long
,float
,double
, oudecimal
. - De para , , ,
long
ulong
,float
, ,double
oudecimal
.uint
int
ushort
- De
int
paralong
,float
,double
, oudecimal
. - De
uint
paralong
,ulong
,float
,double
, oudecimal
. - De
long
parafloat
,double
, oudecimal
. - De
ulong
parafloat
,double
, oudecimal
. - De
char
paraushort
,int
,uint
,long
ulong
, ,float
, ,double
oudecimal
. - De
float
paradouble
.
As conversões de int
, uint
, long
ou ulong
para float
e de long
ou ulong
para double
podem causar uma perda de precisão, mas nunca causarão uma perda de magnitude. As outras conversões numéricas implícitas nunca perdem nenhuma informação.
Não há conversões implícitas predefinidas para o char
tipo, portanto, os valores dos outros tipos integrais não são convertidos automaticamente para o char
tipo.
10.2.4 Conversões de enumeração implícitas
Uma conversão de enumeração implícita permite que um constant_expression (§12.23) com qualquer tipo inteiro e o valor zero seja convertido em qualquer enum_type e em qualquer nullable_value_type cujo tipo subjacente seja um enum_type. No último caso, a conversão é avaliada convertendo-se para o enum_type subjacente e encapsulando o resultado (§8.3.12).
10.2.5 Conversões de cadeia de caracteres interpoladas implícitas
Uma conversão de cadeia de caracteres interpolada implícita permite que um interpolated_string_expression (§12.8.3) seja convertido em System.IFormattable
ou System.FormattableString
(que implementa System.IFormattable
).
Quando essa conversão é aplicada, um valor de cadeia de caracteres não é composto a partir da cadeia de caracteres interpolada. Em vez disso, uma instância de System.FormattableString
é criada, conforme descrito em mais detalhes em §12.8.3.
10.2.6 Conversões implícitas anuláveis
As conversões implícitas anuláveis são aquelas conversões anuláveis (§10.6.1) derivadas de conversões predefinidas implícitas.
10.2.7 Conversões literais nulas
Existe uma conversão implícita do null
literal para qualquer tipo de referência ou tipo de valor anulável. Essa conversão produzirá uma referência nula se o tipo de destino for um tipo de referência ou o valor nulo (§8.3.12) do tipo de valor anulável fornecido.
10.2.8 Conversões de referência implícitas
As conversões de referência implícitas são:
- De qualquer reference_type para
object
edynamic
. - De qualquer class_type
S
para qualquer class_typeT
, desde queS
seja derivado deT
. - De qualquer class_type
S
a qualquer interface_typeT
, implementosT
fornecidosS
. - De qualquer interface_type
S
para qualquer interface_typeT
, desde queS
seja derivado deT
. - De um array_type
S
com um tipoSᵢ
de elemento para um array_typeT
com um tipoTᵢ
de elemento, desde que todos os itens a seguir sejam verdadeiros:S
eT
diferem apenas no tipo de elemento. Em outras palavras,S
eT
têm o mesmo número de dimensões.- Existe uma conversão de referência implícita de
Sᵢ
paraTᵢ
.
- De um tipo
S[]
de matriz unidimensional paraSystem.Collections.Generic.IList<T>
,System.Collections.Generic.IReadOnlyList<T>
, e suas interfaces base, desde que haja uma identidade implícita ou conversão de referência deS
paraT
. - De qualquer array_type para
System.Array
e as interfaces que implementa. - De qualquer delegate_type para
System.Delegate
e as interfaces que implementa. - Do literal nulo (§6.4.5.7) para qualquer tipo de referência.
- De qualquer reference_type para um reference_type
T
se ele tiver uma conversão implícita de identidade ou referência para um reference_typeT₀
eT₀
tiver uma conversão de identidade paraT
. - De qualquer reference_type para um tipo
T
de interface ou delegado se ele tiver uma identidade implícita ou conversão de referência para um tipoT₀
de interface ou delegado eT₀
for conversível por variância (§18.2.3.3) paraT
. - Conversões implícitas envolvendo parâmetros de tipo que são conhecidos por serem tipos de referência. Consulte §10.2.12 para obter mais detalhes sobre conversões implícitas envolvendo parâmetros de tipo.
As conversões de referência implícitas são aquelas conversões entre reference_types que podem ser comprovadas como sempre bem-sucedidas e, portanto, não exigem verificações em tempo de execução.
As conversões de referência, implícitas ou explícitas, nunca alteram a identidade referencial do objeto que está sendo convertido.
Observação: em outras palavras, embora uma conversão de referência possa alterar o tipo da referência, ela nunca altera o tipo ou o valor do objeto ao qual está sendo referido. nota final
10.2.9 Conversões de boxe
Uma conversão de boxe permite que um value_type seja implicitamente convertido em um reference_type. Existem as seguintes conversões de boxe:
- De qualquer value_type para o tipo
object
. - De qualquer value_type para o tipo
System.ValueType
. - De qualquer enum_type para o tipo
System.Enum
. - De qualquer non_nullable_value_type a qualquer interface_type implementado pelo non_nullable_value_type.
- De qualquer non_nullable_value_type para qualquer interface_type
I
tal que haja uma conversão de boxe do non_nullable_value_type para outro interface_typeI₀
, eI₀
tenha uma conversão de identidade paraI
. - De qualquer non_nullable_value_type para qualquer interface_type
I
tal que haja uma conversão de boxe do non_nullable_value_type para outro interface_typeI₀
, eI₀
seja conversível em variância (§18.2.3.3) paraI
. - De qualquer nullable_value_type para qualquer reference_type onde há uma conversão de boxe do tipo subjacente do nullable_value_type para o reference_type.
- De um parâmetro de tipo que não é conhecido por ser um tipo de referência para qualquer tipo, de modo que a conversão seja permitida por §10.2.12.
A conversão boxing de um valor de um tipo de valor não anulável consiste em alocar uma instância de objeto e copiar o valor para essa instância.
A conversão boxing de um valor de um nullable_value_type produzirá uma referência nula se for o valor nulo (HasValue
for false) ou o resultado de desencapsular e converter o valor subjacente caso contrário.
Nota: O processo de boxing pode ser imaginado em termos da existência de uma classe boxing para cada tipo de valor. Por exemplo, considere a implementação de
struct S
uma interfaceI
, com uma classe de boxe chamadaS_Boxing
.interface I { void M(); } struct S : I { public void M() { ... } } sealed class S_Boxing : I { S value; public S_Boxing(S value) { this.value = value; } public void M() { value.M(); } }
A conversão boxing de um valor
v
do tipoS
agora consiste em executar a expressãonew S_Boxing(v)
e retornar a instância resultante como um valor do tipo de destino da conversão. Assim, as declaraçõesS s = new S(); object box = s;
pode ser pensado como semelhante a:
S s = new S(); object box = new S_Boxing(s);
O tipo de boxe imaginado descrito acima não existe de fato. Em vez disso, um valor de tipo
S
em caixa tem o tipoS
de tempo de execução e uma verificação de tipo de tempo de execução usando ois
operador com um tipo de valor como o operando direito testa se o operando esquerdo é uma versão em caixa do operando direito. Por exemplo,int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }
produzirá o seguinte:
Box contains an int
Uma conversão de boxe implica fazer uma cópia do valor que está sendo encaixotado. Isso é diferente de uma conversão de um reference_type para tipo
object
, em que o valor continua a fazer referência à mesma instância e simplesmente é considerado como o tipoobject
menos derivado . Por exemplo, o seguintestruct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { void M() { Point p = new Point(10, 10); object box = p; p.x = 20; Console.Write(((Point)box).x); } }
produzirá o valor 10 no console porque a operação de encaixotamento implícita que ocorre na atribuição de
p
tobox
faz com que o valor dep
seja copiado. Se tivessePoint
sido declarado umclass
, o valor 20 seria gerado porquep
ebox
faria referência à mesma instância.A analogia de uma aula de boxe não deve ser usada como mais do que uma ferramenta útil para imaginar como o boxe funciona conceitualmente. Existem inúmeras diferenças sutis entre o comportamento descrito por esta especificação e o comportamento que resultaria da implementação do boxe exatamente dessa maneira.
nota final
10.2.10 Conversões dinâmicas implícitas
Existe uma conversão dinâmica implícita de uma expressão do tipo dinâmico para qualquer tipo T
. A conversão é associada dinamicamente §12.3.3, o que significa que uma conversão implícita será procurada em tempo de execução do tipo de tempo de execução da expressão para T
. Se nenhuma conversão for encontrada, uma exceção de tempo de execução será lançada.
Essa conversão implícita aparentemente viola o conselho no início do §10.2 de que uma conversão implícita nunca deve causar uma exceção. No entanto, não é a conversão em si, mas a descoberta da conversão que causa a exceção. O risco de exceções em tempo de execução é inerente ao uso da associação dinâmica. Se a associação dinâmica da conversão não for desejada, a expressão poderá ser convertida primeiro em object
, e depois no tipo desejado.
Exemplo: o seguinte ilustra as conversões dinâmicas implícitas:
object o = "object"; dynamic d = "dynamic"; string s1 = o; // Fails at compile-time – no conversion exists string s2 = d; // Compiles and succeeds at run-time int i = d; // Compiles but fails at run-time – no conversion exists
As atribuições e
s2
i
ambas empregam conversões dinâmicas implícitas, em que a associação das operações é suspensa até o tempo de execução. Em tempo de execução, as conversões implícitas são buscadas do tipo de tempo de execução de (string
) para o tipo ded
destino. Uma conversão é encontrada parastring
, mas não paraint
.exemplo de fim
10.2.11 Conversões de expressão constante implícita
Uma conversão de expressão constante implícita permite as seguintes conversões:
- Um constant_expression (§12.23) do tipo
int
pode ser convertido para o tiposbyte
,byte
,short
,ushort
,uint
, ouulong
, desde que o valor do constant_expression esteja dentro do intervalo do tipo de destino. - Um constant_expression do tipo
long
pode ser convertido em tipoulong
, desde que o valor do constant_expression não seja negativo.
10.2.12 Conversões implícitas envolvendo parâmetros de tipo
Para um type_parameter T
conhecido por ser um tipo de referência (§15.2.5), existem as seguintes conversões de referência implícitas (§10.2.8):
- De
T
para sua classeC
base efetiva , deT
para qualquer classe base de , e deT
para qualquer interface implementadaC
porC
. - De
T
para um interface_typeI
noT
conjunto de interface efetiva do e deT
para qualquer interface base deI
. - De para um parâmetro de
T
tipo fornecido queT
depende deU
(§15.2.5U
).Observação: como
T
é conhecido por ser um tipo de referência, dentro do escopo deT
, o tipo de tempo de execução sempre será um tipo deU
referência, mesmo queU
não seja conhecido por ser um tipo de referência em tempo de compilação. nota final - Do literal nulo (§6.4.5.7) para T.
Para um type_parameter T
que não é conhecido por ser um tipo de referência §15.2.5, as seguintes conversões envolvendo T
são consideradas conversões de boxing (§10.2.9) em tempo de compilação. Em tempo de execução, if T
é um tipo de valor, a conversão é executada como uma conversão boxing. Em tempo de execução, if T
é um tipo de referência, a conversão é executada como uma conversão de referência implícita ou conversão de identidade.
- De
T
para sua classeC
base efetiva , deT
para qualquer classe base de , e deT
para qualquer interface implementadaC
porC
.Nota:
C
será um dos tiposSystem.Object
,System.ValueType
, ouSystem.Enum
(caso contrárioT
, seria conhecido como um tipo de referência). nota final - De
T
para um interface_typeI
noT
conjunto de interface efetiva do e deT
para qualquer interface base deI
.
Para um type_parameter T
que não é conhecido por ser um tipo de referência, há uma conversão implícita de para um parâmetro U
de T
tipo fornecido T
depende de U
. Em tempo de execução, if T
é um tipo de valor e U
é um tipo de referência, a conversão é executada como uma conversão boxing. Em tempo de execução, se ambos e U
T
forem tipos de valor, então T
e U
são necessariamente do mesmo tipo e nenhuma conversão é executada. Em tempo de execução, if T
é um tipo de referência, então U
também é necessariamente um tipo de referência e a conversão é executada como uma conversão de referência implícita ou conversão de identidade (§15.2.5).
As seguintes conversões implícitas existem para um determinado parâmetro T
de tipo:
- From para um tipo de
T
referência se ele tiver uma conversão implícita para um tipoS₀
de referência eS₀
tiver uma conversão de identidade paraS
.S
Em tempo de execução, a conversão é executada da mesma forma que a conversão paraS₀
. - De para um tipo de
T
interface se ele tiver uma conversão implícita em um tipoI₀
de interface eI₀
for conversível por variância emI
(§18.2.3.3).I
Em tempo de execução, ifT
é um tipo de valor, a conversão é executada como uma conversão boxing. Caso contrário, a conversão será executada como uma conversão de referência implícita ou conversão de identidade.
Em todos os casos, as regras garantem que uma conversão seja executada como uma conversão boxing se e somente se, em tempo de execução, a conversão for de um tipo de valor para um tipo de referência.
10.2.13 Conversões de tupla implícitas
Existe uma conversão implícita de uma expressão E
de tupla para um tipo T
de tupla se E
tiver a mesma aridade que T
e existe uma conversão implícita de cada elemento em E
para o tipo de elemento correspondente em T
. A conversão é realizada criando uma instância do tipo correspondente System.ValueTuple<...>
de T
e inicializando cada um de seus campos em ordem da esquerda para a direita, avaliando a expressão de elemento de tupla correspondente de E
, convertendo-a para o tipo de elemento correspondente usando T
a conversão implícita encontrada e inicializando o campo com o resultado.
Se um nome de elemento na expressão de tupla não corresponder a um nome de elemento correspondente no tipo de tupla, um aviso deve ser emitido.
Exemplo:
(int, string) t1 = (1, "One"); (byte, string) t2 = (2, null); (int, string) t3 = (null, null); // Error: No conversion (int i, string s) t4 = (i: 4, "Four"); (int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored
As declarações de
t1
,t2
,t4
et5
são todas válidas, pois existem conversões implícitas das expressões de elemento para os tipos de elemento correspondentes. A declaração det3
é inválida, porque não há conversão denull
paraint
. A declaração de causa um aviso porque os nomes det5
elemento na expressão de tupla são diferentes daqueles no tipo de tupla.exemplo de fim
10.2.14 Conversões implícitas definidas pelo usuário
Uma conversão implícita definida pelo usuário consiste em uma conversão implícita padrão opcional, seguida pela execução de um operador de conversão implícita definido pelo usuário, seguida por outra conversão implícita padrão opcional. As regras exatas para avaliar conversões implícitas definidas pelo usuário são descritas em §10.5.4.
10.2.15 Conversões de funções anônimas e conversões de grupos de métodos
Funções anônimas e grupos de métodos não têm tipos em si mesmos, mas podem ser convertidos implicitamente em tipos delegados. Além disso, algumas expressões lambda podem ser convertidas implicitamente em tipos de árvore de expressão. As conversões de função anônima são descritas com mais detalhes na seção 10.7 e as conversões de grupo de métodos na seção 10.8.
10.2.16 Conversões literais padrão
Existe uma conversão implícita de um default_literal (§12.8.21) para qualquer tipo. Essa conversão produz o valor padrão (§9.3) do tipo inferido.
10.2.17 Conversões de lançamento implícitas
Embora as expressões throw não tenham um tipo, elas podem ser convertidas implicitamente em qualquer tipo.
10.3 Conversões explícitas
10.3.1 Geral
As seguintes conversões são classificadas como conversões explícitas:
- Todas as conversões implícitas (§10.2)
- Conversões numéricas explícitas (§10.3.2)
- Conversões de enumeração explícitas (§10.3.3)
- Conversões anuláveis explícitas (§10.3.4)
- Conversões de tupla explícitas (§10.3.6)
- Conversões de referência explícita (§10.3.5)
- Conversões explícitas de interface
- Conversões de unboxing (§10.3.7)
- Conversões de parâmetro de tipo explícito (§10.3.8)
- Conversões explícitas definidas pelo usuário (§10.3.9)
Conversões explícitas podem ocorrer em expressões de conversão (§12.9.7).
O conjunto de conversões explícitas inclui todas as conversões implícitas.
Observação: isso, por exemplo, permite que uma conversão explícita seja usada quando existe uma conversão de identidade implícita, a fim de forçar a seleção de uma sobrecarga de método específica. nota final
As conversões explícitas que não são conversões implícitas são conversões que nem sempre podem ser comprovadas como bem-sucedidas, conversões que possivelmente perdem informações e conversões entre domínios de tipos suficientemente diferentes para merecer notação explícita.
10.3.2 Conversões numéricas explícitas
As conversões numéricas explícitas são as conversões de um numeric_type para outro numeric_type para as quais ainda não existe uma conversão numérica implícita (§10.2.3):
- De
sbyte
parabyte
,ushort
,uint
,ulong
, ouchar
. - De
byte
parasbyte
ouchar
. - De
short
parasbyte
,byte
,ushort
,uint
,ulong
, ouchar
. - De
ushort
parasbyte
,byte
,short
, ouchar
. - De para , , ,
short
ushort
,uint
, ,ulong
ouchar
.byte
sbyte
int
- De
uint
parasbyte
,byte
,short
,ushort
,int
, ouchar
. - De
long
parasbyte
,byte
,short
,ushort
int
, ,uint
, ,ulong
ouchar
. - De
ulong
parasbyte
,byte
,short
,ushort
int
, ,uint
, ,long
ouchar
. - De
char
parasbyte
,byte
, oushort
. - De para , , ,
short
ushort
,int
uint
, ,long
, ,ulong
, , oudecimal
char
.byte
sbyte
float
- De para , , ,
short
ushort
,uint
int
, ,ulong
long
,char
, , ou .decimal
float
byte
sbyte
double
- De para , , ,
short
ushort
,uint
int
, ,ulong
long
,char
, , ou .double
float
byte
sbyte
decimal
Como as conversões explícitas incluem todas as conversões numéricas implícitas e explícitas, é sempre possível converter de qualquer numeric_type para qualquer outro numeric_type usando uma expressão de conversão (§12.9.7).
As conversões numéricas explícitas possivelmente perdem informações ou possivelmente fazem com que exceções sejam lançadas. Uma conversão numérica explícita é processada da seguinte maneira:
- Para uma conversão de um tipo integral para outro tipo integral, o processamento depende do contexto de verificação de estouro (§12.8.20) no qual a conversão ocorre:
- Em um
checked
contexto, a conversão será bem-sucedida se o valor do operando de origem estiver dentro do intervalo do tipo de destino, mas lançará umSystem.OverflowException
se o valor do operando de origem estiver fora do intervalo do tipo de destino. - Em um
unchecked
contexto, a conversão sempre é bem-sucedida e prossegue da seguinte maneira.- Se o tipo de origem for maior que o tipo de destino, o valor de origem será truncado descartando seus bits "extras" mais significativos. O resultado é então tratado como um valor do tipo de destino.
- Se o tipo de origem for do mesmo tamanho que o tipo de destino, o valor de origem será tratado como um valor do tipo de destino
- Em um
- Para uma conversão de para um tipo integral, o valor de
decimal
origem é arredondado para zero para o valor integral mais próximo e esse valor integral se torna o resultado da conversão. Se o valor integral resultante estiver fora do intervalo do tipo de destino, aSystem.OverflowException
será lançado. - Para uma conversão de
float
oudouble
para um tipo integral, o processamento depende do contexto de verificação de estouro (§12.8.20) no qual a conversão ocorre:- Em um contexto verificado, a conversão ocorre da seguinte maneira:
- Se o valor do operando for NaN ou infinito, a
System.OverflowException
será lançado. - Caso contrário, o operando de origem será arredondado para zero para o valor integral mais próximo. Se esse valor integral estiver dentro do intervalo do tipo de destino, esse valor será o resultado da conversão.
- Caso contrário, uma
System.OverflowException
será gerada.
- Se o valor do operando for NaN ou infinito, a
- Em um contexto não verificado, a conversão sempre é bem-sucedida e prossegue da seguinte maneira.
- Se o valor do operando for NaN ou infinito, o resultado da conversão será um valor não especificado do tipo de destino.
- Caso contrário, o operando de origem será arredondado para zero para o valor integral mais próximo. Se esse valor integral estiver dentro do intervalo do tipo de destino, esse valor será o resultado da conversão.
- Caso contrário, o resultado da conversão será um valor não especificado do tipo de destino.
- Em um contexto verificado, a conversão ocorre da seguinte maneira:
- Para uma conversão de
double
parafloat
, odouble
valor é arredondado para o valor mais próximofloat
. Se odouble
valor for muito pequeno para ser representado como umfloat
, o resultado se tornará zero com o mesmo sinal que o valor. Se a magnitude dodouble
valor for muito grande para representar como umfloat
, o resultado se torna infinito com o mesmo sinal que o valor. Se odouble
valor for NaN, o resultado também será NaN. - Para uma conversão de
float
oudouble
paradecimal
, o valor de origem é convertido emdecimal
representação e arredondado para o número mais próximo, se necessário (§8.3.8).- Se o valor de origem for muito pequeno para ser representado como um
decimal
, o resultado se tornará zero, preservando o sinal do valor original sedecimal
suportar valores zero com sinal. - Se a magnitude do valor de origem for muito grande para ser representada como um
decimal
, ou se esse valor for infinito, o resultado será infinito preservando o sinal do valor original, se a representação decimal der suporte a infinitos; caso contrário, um System.OverflowException será gerado. - Se o valor de origem for NaN, o resultado será NaN se a representação decimal der suporte a NaNs; caso contrário, um System.OverflowException será gerado.
- Se o valor de origem for muito pequeno para ser representado como um
- Para uma conversão de
decimal
parafloat
oudouble
, o valor é arredondadodecimal
para o valor orfloat
mais próximodouble
. Se a magnitude do valor de origem for muito grande para representar no tipo de destino, ou se esse valor for infinito, o resultado será infinito preservando o sinal do valor original. Se o valor de origem for NaN, o resultado será NaN. Embora essa conversão possa perder precisão, ela nunca faz com que uma exceção seja lançada.
Observação: o
decimal
tipo não é necessário para dar suporte a infinitos ou valores NaN, mas pode fazê-lo; seu intervalo pode ser menor que o intervalo defloat
e ,double
mas não é garantido que seja. Paradecimal
representações sem infinitos ou valores NaN, e com um intervalo menor quefloat
, o resultado de uma conversão dedecimal
para oufloat
double
nunca será infinito ou NaN. nota final
10.3.3 Conversões explícitas de enumeração
As conversões de enumeração explícitas são:
- De
sbyte
,byte
,short
,ushort
,int
,char
ulong
uint
long
float
oudecimal
double
para qualquer enum_type. - De qualquer enum_type para
sbyte
,byte
,short
,long
char
ulong
float
int
uint
ushort
,double
decimal
ou . - De qualquer enum_type para qualquer outro enum_type.
Uma conversão de enumeração explícita entre dois tipos é processada tratando qualquer enum_type participante como o tipo subjacente desse enum_type e, em seguida, executando uma conversão numérica implícita ou explícita entre os tipos resultantes.
Exemplo: Dada uma enum_type
E
com e o tipo subjacente de , uma conversão de parabyte
é processadaint
E
como uma conversão numérica explícita (§10.3.2) deint
parabyte
, e uma conversão de paraE
é processadabyte
como uma conversão numérica implícita (§10.2.3) debyte
paraint
. exemplo de fim
10.3.4 Conversões explícitas anuláveis
As conversões anuláveis explícitas são aquelas conversões anuláveis (§10.6.1) derivadas de conversões predefinidas explícitas e implícitas.
10.3.5 Conversões de referência explícitas
As conversões de referência explícitas são:
- De objeto a qualquer outro reference_type.
- De qualquer class_type
S
para qualquer class_typeT
, desde queS
seja uma classe base deT
. - De qualquer class_type
S
a qualquer interface_typeT
, desde queS
não seja lacrado e desdeS
que não implementeT
. - De qualquer interface_type
S
a qualquer class_typeT
, desde queT
não seja selado ou fornecidoT
implementosS
. - De qualquer interface_type
S
para qualquer interface_typeT
, desde queS
não seja derivado deT
. - De um array_type
S
com um tipoSᵢ
de elemento para um array_typeT
com um tipoTᵢ
de elemento, desde que todos os itens a seguir sejam verdadeiros:S
eT
diferem apenas no tipo de elemento. Em outras palavras,S
eT
têm o mesmo número de dimensões.- Existe uma conversão de referência explícita de
Sᵢ
paraTᵢ
.
- De
System.Array
e as interfaces que ele implementa, para qualquer array_type. - De um array_type
S[]
unidimensional paraSystem.Collections.Generic.IList<T>
,System.Collections.Generic.IReadOnlyList<T>
, e suas interfaces básicas, desde que haja uma conversão de identidade ou conversão de referência explícita deS
paraT
. - De
System.Collections.Generic.IList<S>
,System.Collections.Generic.IReadOnlyList<S>
, e suas interfaces base para um tipoT[]
de matriz unidimensional , desde que haja uma conversão de identidade ou conversão de referência explícita deS
para T. - De
System.Delegate
e as interfaces que ele implementa para qualquer delegate_type. - De um tipo
S
de referência para um tipoT
de referência se ele tiver uma conversão de referência explícita de para um tipoT₀
deS
referência eT₀
houver uma conversão de identidade deT₀
paraT
. - De um tipo
S
de referência para um tipoT
de interface ou delegado se houver uma conversão de referência explícita deS
para uma interface ou tipoT₀
delegado eT₀
for conversível de variação ouT
T
for conversível de variação paraT₀
§18.2.3.3. - De para onde é um tipo de delegado genérico,
D<S₁...Sᵥ>
não é compatível ou idêntico aD<T₁...Tᵥ>
, e para cada parâmetroXᵢ
de tipo dosD
seguintes contém:D<X₁...Xᵥ>
D<T₁...Tᵥ>
D<S₁...Sᵥ>
- Se
Xᵢ
é invariante, entãoSᵢ
é idêntico aTᵢ
. - Se
Xᵢ
for covariante, haverá uma conversão de identidade, conversão de referência implícita ou conversão de referência explícita deSᵢ
paraTᵢ
. - Se
Xᵢ
é contravariante, entãoSᵢ
eTᵢ
são idênticos ou ambos os tipos de referência.
- Se
- Conversões explícitas envolvendo parâmetros de tipo que são conhecidos por serem tipos de referência. Para obter mais detalhes sobre conversões explícitas envolvendo parâmetros de tipo, consulte §10.3.8.
As conversões de referência explícitas são aquelas conversões entre reference_types que exigem verificações de tempo de execução para garantir que estejam corretas.
Para que uma conversão de referência explícita seja bem-sucedida em tempo de execução, o valor do operando de origem deve ser null
, ou o tipo do objeto referenciado pelo operando de origem deve ser um tipo que pode ser convertido no tipo de destino por uma conversão de referência implícita (§10.2.8). Se uma conversão de referência explícita falhar, a System.InvalidCastException
será gerado.
Observação: as conversões de referência, implícitas ou explícitas, nunca alteram o valor da referência em si (§8.2.1), apenas seu tipo; nem alteram o tipo ou o valor do objeto que está sendo referenciado. nota final
10.3.6 Conversões explícitas de tupla
Existe uma conversão explícita de uma expressão E
de tupla para um tipo T
de tupla se E
tiver a mesma aridade que T
e existe uma conversão implícita ou explícita de cada elemento para E
o tipo de elemento correspondente em T
. A conversão é realizada criando uma instância do tipo correspondente System.ValueTuple<...>
de T
e inicializando cada um de seus campos em ordem da esquerda para a direita, avaliando a expressão de elemento de tupla correspondente de E
, convertendo-a para o tipo de elemento correspondente usando T
a conversão explícita encontrada e inicializando o campo com o resultado.
10.3.7 Conversões de unboxing
Uma conversão de unboxing permite que um reference_type seja explicitamente convertido em um value_type. Existem as seguintes conversões de unboxing:
- Do tipo
object
a qualquer value_type. - Do tipo
System.ValueType
a qualquer value_type. - Do tipo
System.Enum
a qualquer enum_type. - De qualquer interface_type a qualquer non_nullable_value_type que implemente o interface_type.
- De qualquer interface_type
I
para qualquer non_nullable_value_type em que haja uma conversão de unboxing de um interface_typeI₀
para o tipo non_nullable_value e uma conversão de identidade deI
paraI₀
. - De qualquer interface_type
I
para qualquer non_nullable_value_type em que há uma conversão de unboxing de um interface_typeI₀
para o non_nullable_value_type eI₀
é variance_convertible paraI
ouI
é conversível de variância paraI₀
(§18.2.3.3). - De qualquer reference_type para qualquer nullable_value_type em que haja uma conversão de unboxing de reference_type para o non_nullable_value_type subjacente do nullable_value_type.
- De um parâmetro de tipo que não é conhecido por ser um tipo de valor para qualquer tipo, de modo que a conversão seja permitida por §10.3.8.
Uma operação de unboxing para um non_nullable_value_type consiste em primeiro verificar se a instância do objeto é um valor em caixa do non_nullable_value_type fornecido e, em seguida, copiar o valor para fora da instância.
A unboxing para um nullable_value_type produz o valor nulo do nullable_value_type se o operando de origem for null
, ou o resultado encapsulado da unboxing da instância do objeto para o tipo subjacente do nullable_value_type caso contrário.
Observação: Referindo-se à classe de boxe imaginária descrita em §10.2.9, uma conversão de unboxing de uma caixa de objeto em um value_type
S
consiste em executar a expressão((S_Boxing)box).value
. Assim, as declaraçõesobject box = new S(); S s = (S)box;
conceitualmente correspondem a
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;
nota final
Para que uma conversão de unboxing em um determinado non_nullable_value_type seja bem-sucedida em tempo de execução, o valor do operando de origem deve ser uma referência a um valor em caixa desse non_nullable_value_type. Se o operando de origem for null
a System.NullReferenceException
, será gerado. Se o operando de origem for uma referência a um objeto incompatível, a System.InvalidCastException
será gerado.
Para que uma conversão de unboxing em um determinado nullable_value_type seja bem-sucedida em tempo de execução, o valor do operando de origem deve ser nulo ou uma referência a um valor em caixa do non_nullable_value_type subjacente do nullable_value_type. Se o operando de origem for uma referência a um objeto incompatível, a System.InvalidCastException
será gerado.
10.3.8 Conversões explícitas envolvendo parâmetros de tipo
Para um type_parameter T
que é conhecido por ser um tipo de referência (§15.2.5), existem as seguintes conversões de referência explícitas (§10.3.5):
- Da classe
C
base efetiva deT
paraT
e de qualquer classe base deC
paraT
. - De qualquer interface_type a
T
. - De
T
para qualquer interface_typeI
desde que ainda não haja uma conversão de referência implícita deT
paraI
. - De um type_parameter
U
para desdeT
queT
dependa deU
(§15.2.5).Observação: como
T
é conhecido por ser um tipo de referência, dentro do escopo deT
, o tipo de tempo de execução de U sempre será um tipo de referência, mesmo queU
não seja conhecido por ser um tipo de referência em tempo de compilação. nota final
Para um type_parameter T
que não é conhecido como um tipo de referência (§15.2.5), as seguintes conversões envolvendo T
são consideradas conversões de unboxing (§10.3.7) em tempo de compilação. Em tempo de execução, if T
é um tipo de valor, a conversão é executada como uma conversão de unboxing. Em tempo de execução, if T
é um tipo de referência, a conversão é executada como uma conversão de referência explícita ou conversão de identidade.
- Da classe
C
base efetiva deT
paraT
e de qualquer classe base deC
paraT
.Nota: C será um dos tipos
System.Object
,System.ValueType
, ouSystem.Enum
(caso contrárioT
, seria conhecido como um tipo de referência). nota final - De qualquer interface_type a
T
.
Para um type_parameter T
que não é conhecido por ser um tipo de referência (§15.2.5), existem as seguintes conversões explícitas:
- De
T
para qualquer interface_typeI
desde que ainda não haja uma conversão implícita deT
paraI
. Essa conversão consiste em uma conversão de boxing implícita (§10.2.9) deT
para seguidaobject
por uma conversão de referência explícita deobject
paraI
. Em tempo de execução, ifT
é um tipo de valor, a conversão é executada como uma conversão boxing seguida por uma conversão de referência explícita. Em tempo de execução, ifT
é um tipo de referência, a conversão é executada como uma conversão de referência explícita. - De um parâmetro
U
de tipo paraT
provided queT
depende deU
(§15.2.5). Em tempo de execução, ifT
é um tipo de valor eU
é um tipo de referência, a conversão é executada como uma conversão de unboxing. Em tempo de execução, se ambos eU
T
forem tipos de valor, entãoT
eU
são necessariamente do mesmo tipo e nenhuma conversão é executada. Em tempo de execução, ifT
é um tipo de referência, entãoU
também é necessariamente um tipo de referência e a conversão é executada como uma conversão de referência explícita ou conversão de identidade.
Em todos os casos, as regras garantem que uma conversão seja executada como uma conversão de unboxing se e somente se em tempo de execução a conversão for de um tipo de referência para um tipo de valor.
As regras acima não permitem uma conversão explícita direta de um parâmetro de tipo irrestrito para um tipo não interface, o que pode ser surpreendente. A razão para essa regra é evitar confusão e tornar clara a semântica de tais conversões.
Exemplo: considere a seguinte declaração:
class X<T> { public static long F(T t) { return (long)t; // Error } }
Se a conversão explícita direta de
t
paralong
fosse permitida, seria facilmente de se esperar queX<int>.F(7)
isso retornasse7L
. No entanto, isso não aconteceria, porque as conversões numéricas padrão só são consideradas quando os tipos são conhecidos como numéricos no tempo de associação. Para tornar a semântica clara, o exemplo acima deve ser escrito:class X<T> { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } }
Esse código agora será compilado, mas a execução
X<int>.F(7)
lançará uma exceção em tempo de execução, já que uma caixa não pode ser convertidaint
diretamente em umlong
.exemplo de fim
10.3.9 Conversões explícitas definidas pelo usuário
Uma conversão explícita definida pelo usuário consiste em uma conversão explícita padrão opcional, seguida pela execução de um operador de conversão implícito ou explícito definido pelo usuário, seguido por outra conversão explícita padrão opcional. As regras exatas para avaliar conversões explícitas definidas pelo usuário são descritas em §10.5.5.
10.4 Conversões padrão
10.4.1 Geral
As conversões padrão são aquelas predefinidas que podem ocorrer como parte de uma conversão definida pelo usuário.
10.4.2 Conversões implícitas padrão
As seguintes conversões implícitas são classificadas como conversões implícitas padrão:
- Conversões de identidade (§10.2.2)
- Conversões numéricas implícitas (§10.2.3)
- Conversões implícitas anuláveis (§10.2.6)
- Conversões literais nulas (§10.2.7)
- Conversões de referência implícitas (§10.2.8)
- Conversões de boxing (§10.2.9)
- Conversões de expressão constante implícita (§10.2.11)
- Conversões implícitas envolvendo parâmetros de tipo (§10.2.12)
As conversões implícitas padrão excluem especificamente as conversões implícitas definidas pelo usuário.
10.4.3 Conversões explícitas padrão
As conversões explícitas padrão são todas as conversões implícitas padrão mais o subconjunto das conversões explícitas para as quais existe uma conversão implícita padrão oposta.
Observação: em outras palavras, se existe uma conversão implícita padrão de um tipo
A
para um tipoB
, então existe uma conversão explícita padrão de tipoA
para tipoB
e de tipoB
para tipoA
. nota final
10.5 Conversões definidas pelo usuário
10.5.1 Geral
O C# permite que as conversões implícitas e explícitas predefinidas sejam aumentadas por conversões definidas pelo usuário. As conversões definidas pelo usuário são introduzidas declarando operadores de conversão (§15.10.4) em tipos de classe e struct.
10.5.2 Conversões permitidas definidas pelo usuário
O C# permite que apenas determinadas conversões definidas pelo usuário sejam declaradas. Em particular, não é possível redefinir uma conversão implícita ou explícita já existente.
Para um determinado tipo S
de origem e tipo T
de destino, se S
ou T
forem tipos de valor anuláveis, deixe S₀
e T₀
faça referência a seus tipos subjacentes, caso contrário S₀
, e T₀
são iguais a S
e T
respectivamente. Uma classe ou struct tem permissão para declarar uma conversão de um tipo S
de origem para um tipo T
de destino somente se todos os itens a seguir forem verdadeiros:
S₀
eT₀
são tipos diferentes.- Either
S₀
orT₀
é a classe ou o tipo de struct no qual a declaração do operador ocorre. - Nem
S₀
T₀
é um interface_type. - Excluindo conversões definidas pelo usuário, não existe uma conversão de
S
paraT
ou deT
paraS
.
As restrições que se aplicam às conversões definidas pelo usuário são especificadas em §15.10.4.
10.5.3 Avaliação de conversões definidas pelo usuário
Uma conversão definida pelo usuário converte uma expressão de origem, que pode ter um tipo de origem, em outro tipo, chamado de tipo de destino. A avaliação de uma conversão definida pelo usuário se concentra em encontrar o operador de conversão definido pelo usuário mais específico para a expressão de origem e o tipo de destino. Essa determinação é dividida em várias etapas:
- Encontrar o conjunto de classes e structs a partir do qual os operadores de conversão definidos pelo usuário serão considerados. Esse conjunto consiste no tipo de origem e suas classes base, se o tipo de origem existir, juntamente com o tipo de destino e suas classes base. Para essa finalidade, supõe-se que apenas classes e structs podem declarar operadores definidos pelo usuário e que os tipos que não são de classe não têm classes base. Além disso, se o tipo de origem ou de destino for um tipo de valor anulável, o tipo subjacente será usado.
- A partir desse conjunto de tipos, determinar quais operadores de conversão definidos pelo usuário e levantados são aplicáveis. Para que um operador de conversão seja aplicável, deve ser possível realizar uma conversão padrão (§10.4) da expressão de origem para o tipo de operando do operador, e deve ser possível realizar uma conversão padrão do tipo de resultado do operador para o tipo de destino.
- A partir do conjunto de operadores definidos pelo usuário aplicáveis, determinar qual operador é inequivocamente o mais específico. Em termos gerais, o operador mais específico é o operador cujo tipo de operando é "mais próximo" da expressão de origem e cujo tipo de resultado é "mais próximo" do tipo de destino. Os operadores de conversão definidos pelo usuário são preferenciais aos operadores de conversão elevados. As regras exatas para estabelecer o operador de conversão definido pelo usuário mais específico são definidas nas subcláusulas a seguir.
Depois que um operador de conversão definido pelo usuário mais específico for identificado, a execução real da conversão definida pelo usuário envolve até três etapas:
- Primeiro, se necessário, executar uma conversão padrão da expressão de origem para o tipo de operando do operador de conversão definido pelo usuário ou levantado.
- Em seguida, chamar o operador de conversão definido pelo usuário ou levantado para executar a conversão.
- Por fim, se necessário, executar uma conversão padrão do tipo de resultado do operador de conversão definido pelo usuário para o tipo de destino.
A avaliação de uma conversão definida pelo usuário nunca envolve mais de um operador de conversão definido pelo usuário ou levantado. Em outras palavras, uma conversão de tipo S
para tipo T
nunca executará primeiro uma conversão definida pelo usuário de S
para X
e, em seguida, executará uma conversão definida pelo usuário de X
para T
.
- As definições exatas de avaliação de conversões implícitas ou explícitas definidas pelo usuário são fornecidas nas subcláusulas a seguir. As definições fazem uso dos seguintes termos:
- Se existe uma conversão implícita padrão (§10.4.2) de um tipo
A
para um tipoB
, e se nemA
nemB
são interface_types
, entãoA
é dito ser abrangido porB
, eB
diz-se que abrangeA
. - Se existe uma conversão implícita padrão (§10.4.2) de uma expressão
E
para um tipoB
, e se nemB
nem o tipo deE
(se houver) são interface_types
, entãoE
é dito ser abrangido porB
, eB
diz-se que abrangeE
. - O tipo mais abrangente em um conjunto de tipos é aquele que engloba todos os outros tipos no conjunto. Se nenhum tipo único englobar todos os outros tipos, o conjunto não terá nenhum tipo mais abrangente. Em termos mais intuitivos, o tipo mais abrangente é o tipo "maior" no conjunto - o único tipo para o qual cada um dos outros tipos pode ser convertido implicitamente.
- O tipo mais englobado em um conjunto de tipos é aquele que é abrangido por todos os outros tipos no conjunto. Se nenhum tipo único for abrangido por todos os outros tipos, o conjunto não terá nenhum tipo mais abrangido. Em termos mais intuitivos, o tipo mais englobado é o tipo "menor" no conjunto — o único tipo que pode ser convertido implicitamente em cada um dos outros tipos.
10.5.4 Conversões implícitas definidas pelo usuário
Uma conversão implícita definida pelo usuário de uma expressão E
para um tipo T
é processada da seguinte maneira:
Determine os tipos
S
,S₀
eT₀
.- Se
E
tiver um tipo, sejaS
esse tipo. - Se
S
ouT
são tipos de valor anuláveis, letSᵢ
eTᵢ
be seus tipos subjacentes, caso contrário, letSᵢ
eTᵢ
beS
eT
, respectivamente. - Se
Sᵢ
ouTᵢ
são parâmetros de tipo, letS₀
eT₀
be suas classes base efetivas, caso contrário, letS₀
eT₀
beSₓ
eTᵢ
, respectivamente.
- Se
Encontre o conjunto de tipos,
D
, a partir do qual os operadores de conversão definidos pelo usuário serão considerados. Esse conjunto consiste emS₀
(seS₀
existir e for uma classe ou struct), as classes base deS₀
(seS₀
existir e for uma classe) eT₀
(seT₀
for uma classe ou struct). Um tipo será adicionado ao conjuntoD
somente se não existir uma conversão de identidade para outro tipo já incluído no conjunto.Localize o conjunto de operadores de conversão definidos pelo usuário e levantados aplicáveis,
U
. Esse conjunto consiste nos operadores de conversão implícitos definidos pelo usuário e levantados declarados pelas classes ou structs que convertem deD
um tipo abrangenteE
para um tipo englobado porT
. SeU
estiver vazio, a conversão será indefinida e ocorrerá um erro em tempo de compilação.- Se
S
existe e qualquer um dos operadores emU
convert fromS
, entãoSₓ
éS
. - Caso contrário,
Sₓ
é o tipo mais englobado no conjunto combinado de tipos de origem dos operadores emU
. Se exatamente um tipo mais englobado não puder ser encontrado, a conversão será ambígua e ocorrerá um erro em tempo de compilação.
- Se
Encontre o tipo de destino mais específico,
Tₓ
, dos operadores emU
:- Se qualquer um dos operadores em
U
convert paraT
, entãoTₓ
éT
. - Caso contrário,
Tₓ
é o tipo mais abrangente no conjunto combinado de tipos de destino dos operadores noU
. Se exatamente um tipo mais abrangente não puder ser encontrado, a conversão será ambígua e ocorrerá um erro em tempo de compilação.
- Se qualquer um dos operadores em
Encontre o operador de conversão mais específico:
- Se
U
contiver exatamente um operador de conversão definido pelo usuário que converte deSₓ
paraTₓ
, esse será o operador de conversão mais específico. - Caso contrário, se
U
contiver exatamente um operador de conversão levantado que converte deSₓ
paraTₓ
, esse será o operador de conversão mais específico. - Caso contrário, a conversão será ambígua e ocorrerá um erro em tempo de compilação.
- Se
Por fim, aplique a conversão:
- Se E ainda não tiver o tipo
Sₓ
, uma conversão implícita padrão de paraSₓ
será executadaE
. - O operador de conversão mais específico é chamado para converter de
Sₓ
paraTₓ
. - Se
Tₓ
nãoT
for , uma conversão implícita padrão deTₓ
para seráT
executada.
- Se E ainda não tiver o tipo
Existe uma conversão implícita definida pelo usuário de um tipo S
para um tipo T
se existir uma conversão implícita definida pelo usuário de uma variável do tipo S
para T
.
10.5.5 Conversões explícitas definidas pelo usuário
Uma conversão explícita definida pelo usuário de uma expressão E
para um tipo T
é processada da seguinte maneira:
- Determine os tipos
S
,S₀
eT₀
.- Se
E
tiver um tipo, sejaS
esse tipo. - Se
S
ouT
são tipos de valor anuláveis, letSᵢ
eTᵢ
be seus tipos subjacentes, caso contrário, letSᵢ
eTᵢ
beS
eT
, respectivamente. - Se
Sᵢ
ouTᵢ
são parâmetros de tipo, letS₀
eT₀
be suas classes base efetivas, caso contrário, letS₀
eT₀
beSᵢ
eTᵢ
, respectivamente.
- Se
- Encontre o conjunto de tipos,
D
, a partir do qual os operadores de conversão definidos pelo usuário serão considerados. Esse conjunto consiste emS₀
(seS₀
existir e for uma classe ou struct), as classes base deS₀
(seS₀
existir e for uma classe),T₀
(seT₀
for uma classe ou struct) e as classes base deT₀
(ifT₀
for uma classe).A
type será adicionado ao conjuntoD
somente se não existir uma conversão de identidade para outro tipo já incluído no conjunto. - Localize o conjunto de operadores de conversão definidos pelo usuário e levantados aplicáveis,
U
. Esse conjunto consiste nos operadores de conversão implícitos ou explícitos definidos pelo usuário e levantados declarados pelas classes ou structs que convertemD
de um tipo que abrangeE
ou engloba (S
se existir) para um tipo que abrange ou engloba porT
. SeU
estiver vazio, a conversão será indefinida e ocorrerá um erro em tempo de compilação. - Encontre o tipo de fonte mais específico,
Sₓ
, dos operadores emU
:- Se S existe e qualquer um dos operadores em
U
convert fromS
, entãoSₓ
éS
. - Caso contrário, se qualquer um dos operadores converter
U
de tipos que abrangemE
, entãoSₓ
é o tipo mais englobado no conjunto combinado de tipos de origem desses operadores. Se nenhum tipo mais englobado puder ser encontrado, a conversão será ambígua e ocorrerá um erro em tempo de compilação. - Caso contrário,
Sₓ
é o tipo mais abrangente no conjunto combinado de tipos de origem dos operadores noU
. Se exatamente um tipo mais abrangente não puder ser encontrado, a conversão será ambígua e ocorrerá um erro em tempo de compilação.
- Se S existe e qualquer um dos operadores em
- Encontre o tipo de destino mais específico,
Tₓ
, dos operadores emU
:- Se qualquer um dos operadores em
U
convert paraT
, entãoTₓ
éT
. - Caso contrário, se qualquer um dos operadores for
U
convertido em tipos que são englobados porT
, entãoTₓ
é o tipo mais abrangente no conjunto combinado de tipos de destino desses operadores. Se exatamente um tipo mais abrangente não puder ser encontrado, a conversão será ambígua e ocorrerá um erro em tempo de compilação. - Caso contrário,
Tₓ
é o tipo mais abrangente no conjunto combinado de tipos de destino dos operadores noU
. Se nenhum tipo mais englobado puder ser encontrado, a conversão será ambígua e ocorrerá um erro em tempo de compilação.
- Se qualquer um dos operadores em
- Encontre o operador de conversão mais específico:
- Se U contiver exatamente um operador de conversão definido pelo usuário que converte de
Sₓ
paraTₓ
, esse será o operador de conversão mais específico. - Caso contrário, se
U
contiver exatamente um operador de conversão levantado que converte deSₓ
paraTₓ
, esse será o operador de conversão mais específico. - Caso contrário, a conversão será ambígua e ocorrerá um erro em tempo de compilação.
- Se U contiver exatamente um operador de conversão definido pelo usuário que converte de
- Por fim, aplique a conversão:
- Se
E
ainda não tiver o tipoSₓ
, uma conversão explícita padrão de E paraSₓ
será executada. - O operador de conversão definido pelo usuário mais específico é chamado para converter de
Sₓ
paraTₓ
. - Se
Tₓ
nãoT
for , uma conversão explícita padrão de paraT
será executadaTₓ
.
- Se
Uma conversão explícita definida pelo usuário de um tipo S
para um tipo T
existe se existir uma conversão explícita definida pelo usuário de uma variável do tipo S
para T
.
10.6 Conversões envolvendo tipos anuláveis
10.6.1 Conversões anuláveis
As conversões anuláveis permitem que conversões predefinidas que operam em tipos de valor não anuláveis também sejam usadas com formulários anuláveis desses tipos. Para cada uma das conversões implícitas ou explícitas predefinidas que convertem de um tipo S
de valor não anulável para um tipo T
de valor não anulável (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 e §10.3.3), existem as seguintes conversões anuláveis:
- Uma conversão implícita ou explícita de
S?
paraT?
- Uma conversão implícita ou explícita de
S
paraT?
- Uma conversão explícita de
S?
paraT
.
Uma conversão anulável é classificada como uma conversão implícita ou explícita.
Determinadas conversões anuláveis são classificadas como conversões padrão e podem ocorrer como parte de uma conversão definida pelo usuário. Especificamente, todas as conversões implícitas anuláveis são classificadas como conversões implícitas padrão (§10.4.2) e as conversões explícitas anuláveis que atendem aos requisitos de §10.4.3 são classificadas como conversões explícitas padrão.
A avaliação de uma conversão anulável com base em uma conversão subjacente de S
para T
prossegue da seguinte maneira:
- Se a conversão anulável for de
S?
paraT?
:- Se o valor de origem for nulo (
HasValue
propriedade éfalse
), o resultado será o valor nulo do tipoT?
. - Caso contrário, a conversão será avaliada como um desempacotamento de
S?
para , seguido pela conversão subjacente deS
paraT
, seguido por um encapsulamento deT
paraT?
S
.
- Se o valor de origem for nulo (
- Se a conversão anulável for de
S
paraT?
, a conversão será avaliada como a conversão subjacente deS
paraT
seguida por um encapsulamento deT
paraT?
. - Se a conversão anulável for de
S?
paraT
, a conversão será avaliada como um desencapsulamento deS?
paraS
seguido pela conversão subjacente deS
paraT
.
10.6.2 Conversões levantadas
Dado um operador de conversão definido pelo usuário que converte de um tipo S
de valor não anulável para um tipo T
de valor não anulável, existe um operador de conversão levantado que converte de S?
para T?
. Esse operador de conversão levantada executa um desempacotamento de S?
para seguido pela conversão definida pelo usuário de S
para T
seguida por um encapsulamento de T
para T?
, exceto que um valor S?
nulo é convertido diretamente em um valor T?
nulo S
. Um operador de conversão levantado tem a mesma classificação implícita ou explícita que seu operador de conversão subjacente definido pelo usuário.
10.7 Conversões de funções anônimas
10.7.1 Geral
Um anonymous_method_expression ou lambda_expression é classificado como uma função anônima (§12.19). A expressão não tem um tipo, mas pode ser convertida implicitamente em um tipo delegado compatível. Algumas expressões lambda também podem ser convertidas implicitamente em um tipo de árvore de expressão compatível.
Especificamente, uma função F
anônima é compatível com um tipo D
de delegado fornecido:
- Se
F
contém um anonymous_function_signature, entãoD
eF
tem o mesmo número de parâmetros. - Se
F
não contiver um anonymous_function_signature, poderáD
ter zero ou mais parâmetros de qualquer tipo, desde que nenhum parâmetro de seja um parâmetro deD
saída. - Se
F
tiver uma lista de parâmetros explicitamente tipada, cada parâmetro emD
terá os mesmos modificadores que o parâmetro correspondente emF
e existe uma conversão de identidade entre o parâmetro correspondente emF
. - Se
F
tiver uma lista de parâmetros digitada implicitamente,D
não tiver parâmetros de referência ou saída. - Se o corpo de
F
for uma expressão eD
tiver um tipo de retorno nulo ouF
for assíncrono eD
tiver um«TaskType»
tipo de retorno (§15.15.1), então quando cada parâmetro deF
receber o tipo do parâmetro correspondente emD
, o corpo deF
é uma expressão válida (w.r.t §12) que seria permitida como um statement_expression (§13.7). - Se o corpo de
F
for um bloco eD
tiver um tipo de retorno nulo ouF
for assíncrono eD
tiver um«TaskType»
tipo de retorno , então quando cada parâmetro deF
receber o tipo do parâmetro correspondente emD
, o corpo deF
é um bloco válido (w.r.t §13.3) no qual nenhumareturn
instrução especifica uma expressão. - Se o corpo de
F
for uma expressão eF
não for assíncrono eD
tiver um tipoT
devoid
não retorno , ouF
for assíncrono eD
tiver um«TaskType»<T>
tipo de retorno (§15.15.1), então quando cada parâmetro deF
receber o tipo do parâmetro correspondente emD
, o corpo deF
é uma expressão válida (w.r.t §12) que é implicitamente conversível em .T
- Se o corpo de
F
for um bloco eF
não for assíncrono eD
tiver um tipoT
de retorno não nulo , ouF
for assíncrono eD
tiver um«TaskType»<T>
tipo de retorno, quando cada parâmetro deF
receber o tipo do parâmetro correspondente emD
, o corpo de é um bloco deF
instrução válido (w.r.t §13.3) com um ponto final não alcançável no qual cada instrução de retorno especifica uma expressão que é implicitamente conversível em .T
Exemplo: Os exemplos a seguir ilustram essas regras:
delegate void D(int x); D d1 = delegate { }; // Ok D d2 = delegate() { }; // Error, signature mismatch D d3 = delegate(long x) { }; // Error, signature mismatch D d4 = delegate(int x) { }; // Ok D d5 = delegate(int x) { return; }; // Ok D d6 = delegate(int x) { return x; }; // Error, return type mismatch delegate void E(out int x); E e1 = delegate { }; // Error, E has an output parameter E e2 = delegate(out int x) { x = 1; }; // Ok E e3 = delegate(ref int x) { x = 1; }; // Error, signature mismatch delegate int P(params int[] a); P p1 = delegate { }; // Error, end of block reachable P p2 = delegate { return; }; // Error, return type mismatch P p3 = delegate { return 1; }; // Ok P p4 = delegate { return "Hello"; }; // Error, return type mismatch P p5 = delegate(int[] a) // Ok { return a[0]; }; P p6 = delegate(params int[] a) // Error, params modifier { return a[0]; }; P p7 = delegate(int[] a) // Error, return type mismatch { if (a.Length > 0) return a[0]; return "Hello"; }; delegate object Q(params int[] a); Q q1 = delegate(int[] a) // Ok { if (a.Length > 0) return a[0]; return "Hello"; };
exemplo de fim
Exemplo: os exemplos a seguir usam um tipo
Func<A,R>
delegado genérico que representa uma função que usa um argumento do tipoA
e retorna um valor do tipoR
:delegate R Func<A,R>(A arg);
Nas atribuições
Func<int,int> f1 = x => x + 1; // Ok Func<int,double> f2 = x => x + 1; // Ok Func<double,int> f3 = x => x + 1; // Error Func<int, Task<int>> f4 = async x => x + 1; // Ok
Os tipos de parâmetro e retorno de cada função anônima são determinados a partir do tipo da variável à qual a função anônima é atribuída.
A primeira atribuição converte com êxito a função anônima no tipo
Func<int,int>
delegado porque, quandox
é dado o tipoint
,x + 1
é uma expressão válida que é implicitamente conversível para o tipoint
.Da mesma forma, a segunda atribuição converte com êxito a função anônima no tipo delegado Func<int,double> porque o resultado de
x + 1
(do tipoint
) é implicitamente conversível para o tipodouble
.No entanto, a terceira atribuição é um erro de tempo de compilação porque, quando
x
é dado o tipodouble
, o resultado dex + 1
(do tipodouble
) não é implicitamente conversível em tipoint
.A quarta atribuição converte com êxito a função assíncrona anônima no tipo
Func<int, Task<int>>
delegado porque o resultado dex + 1
(of typeint
) é implicitamente conversível para o tipoint
de retorno efetivo do lambda assíncrono, que tem um tipoTask<int>
de retorno .exemplo de fim
Uma expressão F
lambda é compatível com um tipo Expression<D>
de árvore de expressão se F
for compatível com o tipo D
delegado . Isso não se aplica a métodos anônimos, apenas expressões lambda.
Funções anônimas podem influenciar a resolução de sobrecarga e participar da inferência de tipo. Consulte §12.6 para obter mais detalhes.
10.7.2 Avaliação de conversões de funções anônimas para tipos delegados
A conversão de uma função anônima em um tipo delegado produz uma instância delegada que faz referência à função anônima e ao conjunto (possivelmente vazio) de variáveis externas capturadas que estão ativas no momento da avaliação. Quando o delegado é invocado, o corpo da função anônima é executado. O código no corpo é executado usando o conjunto de variáveis externas capturadas referenciadas pelo delegado. Um delegate_creation_expression (§12.8.17.6) pode ser usado como uma sintaxe alternativa para converter um método anônimo em um tipo delegado.
A lista de invocação de um delegado produzido a partir de uma função anônima contém uma única entrada. O objeto de destino exato e o método de destino do delegado não são especificados. Em particular, não é especificado se o objeto de destino do delegado é null
, o this
valor do membro da função delimitadora ou algum outro objeto.
As conversões de funções anônimas semanticamente idênticas com o mesmo conjunto (possivelmente vazio) de instâncias de variáveis externas capturadas para os mesmos tipos de delegado são permitidas (mas não obrigatórias) para retornar a mesma instância de delegado. O termo semanticamente idêntico é usado aqui para significar que a execução das funções anônimas irá, em todos os casos, produzir os mesmos efeitos dados os mesmos argumentos. Essa regra permite que códigos como os seguintes sejam otimizados.
delegate double Function(double x);
class Test
{
static double[] Apply(double[] a, Function f)
{
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++)
{
result[i] = f(a[i]);
}
return result;
}
static void F(double[] a, double[] b)
{
a = Apply(a, (double x) => Math.Sin(x));
b = Apply(b, (double y) => Math.Sin(y));
...
}
}
Como os dois delegados de função anônima têm o mesmo conjunto (vazio) de variáveis externas capturadas e como as funções anônimas são semanticamente idênticas, o compilador tem permissão para fazer com que os delegados se refiram ao mesmo método de destino. Na verdade, o compilador tem permissão para retornar a mesma instância delegada de ambas as expressões de função anônimas.
10.7.3 Avaliação de conversões de expressão lambda para tipos de árvore de expressão
A conversão de uma expressão lambda em um tipo de árvore de expressão produz uma árvore de expressão (§8.6). Mais precisamente, a avaliação da conversão da expressão lambda produz uma estrutura de objeto que representa a estrutura da própria expressão lambda.
Nem todas as expressões lambda podem ser convertidas em tipos de árvore de expressão. A conversão para um tipo delegado compatível sempre existe, mas pode falhar em tempo de compilação por motivos definidos pela implementação.
Observação: os motivos comuns para uma expressão lambda não ser convertida em um tipo de árvore de expressão incluem:
- Tem um corpo de bloco
- Tem o
async
modificador- Ele contém um operador de atribuição
- Ele contém um parâmetro de saída ou referência
- Ele contém uma expressão ligada dinamicamente
nota final
10.8 Conversões de grupos de métodos
Existe uma conversão implícita de um grupo de métodos (§12.2) para um tipo de delegado compatível (§20.4). If D
é um tipo delegado e E
é uma expressão classificada como um grupo de métodos, então D
é compatível com E
if e somente if E
contém pelo menos um método aplicável em sua forma normal (§12.6.4.2) a qualquer lista de argumentos (§12.6.2) com tipos e modificadores correspondentes aos tipos de parâmetro e modificadores de D
, conforme descrito a seguir.
A aplicação em tempo de compilação da conversão de um grupo E
de métodos para um tipo D
delegado é descrita a seguir.
- Um único método
M
é selecionado correspondendo a uma invocação de método (§12.8.10.2) do formulárioE(A)
, com as seguintes modificações:- A lista
A
de argumentos é uma lista de expressões, cada uma classificada como uma variável e com o tipo e modificador (in
,out
, ouref
) do parâmetro correspondente no parameter_list deD
— exceto parâmetros do tipodynamic
, onde a expressão correspondente tem o tipoobject
em vez dedynamic
. - Os métodos candidatos considerados são apenas os métodos aplicáveis em sua forma normal e não omitem nenhum parâmetro opcional (§12.6.4.2). Assim, os métodos candidatos são ignorados se forem aplicáveis apenas em sua forma expandida ou se um ou mais de seus parâmetros opcionais não tiverem um parâmetro correspondente em
D
.
- A lista
- Uma conversão é considerada existente se o algoritmo de §12.8.10.2 produzir um único método de melhor que
M
seja compatível (§20.4) comD
. - Se o método
M
selecionado for um método de instância, a expressão de instância associada aE
determinará o objeto de destino do delegado. - Se o método
M
selecionado for um método de extensão indicado por meio de um acesso de membro em uma expressão de instância, essa expressão de instância determinará o objeto de destino do delegado. - O resultado da conversão é um valor do tipo
D
, ou seja, um delegado que se refere ao método selecionado e ao objeto de destino.
Exemplo: o seguinte demonstra conversões de grupo de métodos:
delegate string D1(object o); delegate object D2(string s); delegate object D3(); delegate string D4(object o, params object[] a); delegate string D5(int i); class Test { static string F(object o) {...} static void G() { D1 d1 = F; // Ok D2 d2 = F; // Ok D3 d3 = F; // Error – not applicable D4 d4 = F; // Error – not applicable in normal form D5 d5 = F; // Error – applicable but not compatible } }
A atribuição a
d1
converte implicitamente o grupoF
de métodos em um valor do tipoD1
.A atribuição a
d2
mostra como é possível criar um delegado para um método que tem menos tipos de parâmetro derivados (contravariantes) e um tipo de retorno mais derivado (covariante).A atribuição a
d3
mostra como nenhuma conversão existe se o método não for aplicável.A atribuição a
d4
mostra como o método deve ser aplicável em sua forma normal.A atribuição a
d5
mostra como os tipos de parâmetro e retorno do delegado e do método podem diferir apenas para tipos de referência.exemplo de fim
Assim como acontece com todas as outras conversões implícitas e explícitas, o operador cast pode ser usado para executar explicitamente uma conversão específica.
Exemplo: Assim, o exemplo
object obj = new EventHandler(myDialog.OkClick);
poderia ser escrito
object obj = (EventHandler)myDialog.OkClick;
exemplo de fim
Uma conversão de grupo de métodos pode se referir a um método genérico, especificando explicitamente argumentos de tipo dentro E
de , ou por meio de inferência de tipo (§12.6.3). Se a inferência de tipo for usada, os tipos de parâmetro do delegado serão usados como tipos de argumento no processo de inferência. O tipo de retorno do delegado não é usado para inferência. Se os argumentos de tipo são especificados ou inferidos, eles fazem parte do processo de conversão do grupo de métodos; Esses são os argumentos de tipo usados para invocar o método de destino quando o delegado resultante é invocado.
Exemplo:
delegate int D(string s, int i); delegate int E(); class X { public static T F<T>(string s, T t) {...} public static T G<T>() {...} static void Main() { D d1 = F<int>; // Ok, type argument given explicitly D d2 = F; // Ok, int inferred as type argument E e1 = G<int>; // Ok, type argument given explicitly E e2 = G; // Error, cannot infer from return type } }
exemplo de fim
Os grupos de métodos podem influenciar a resolução de sobrecarga e participar da inferência de tipos. Consulte §12.6 para obter mais detalhes.
A avaliação em tempo de execução de uma conversão de grupo de métodos prossegue da seguinte maneira:
- Se o método selecionado em tempo de compilação for um método de instância ou for um método de extensão que é acessado como um método de instância, o objeto de destino do delegado será determinado a partir da expressão de instância associada a
E
:- A expressão de instância é avaliada. Se essa avaliação causar uma exceção, nenhuma outra etapa será executada.
- Se a expressão de instância for de um reference_type, o valor calculado pela expressão de instância se tornará o objeto de destino. Se o método selecionado for um método de instância e o objeto de destino for
null
, aSystem.NullReferenceException
será lançado e nenhuma outra etapa será executada. - Se a expressão de instância for de um value_type, uma operação de conversão boxing (§10.2.9) será executada para converter o valor em um objeto e esse objeto se tornará o objeto de destino.
- Caso contrário, o método selecionado fará parte de uma chamada de método estático e o objeto de destino do delegado será
null
. - Uma instância delegada do tipo
D
delegado é obtida com uma referência ao método que foi determinado em tempo de compilação e uma referência ao objeto de destino calculado acima, da seguinte maneira:- A conversão é permitida (mas não é necessária) para usar uma instância delegada existente que já contenha essas referências.
- Se uma instância existente não foi reutilizada, uma nova é criada (§20.5). Se não houver memória suficiente disponível para alocar a nova instância, a
System.OutOfMemoryException
será gerado. Caso contrário, a instância será inicializada com as referências fornecidas.
ECMA C# draft specification