Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
10.1 Generalidades
Uma conversão faz com que uma expressão seja convertida ou tratada como sendo de um tipo particular, 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 um elenco explícito é necessário.
Exemplo: Por exemplo, a conversão de tipo
intpara tipolongé implícita, de modo que expressões de tipointpodem ser implicitamente tratadas como tipolong. A conversão oposta, de tipolongpara tipoint, é explícita e, portanto, um elenco explícito é necessário.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to intExemplo final
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 aplica-se 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 final
10.2 Conversões implícitas
10.2.1 Generalidades
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ícitas (§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 boxe (§10.2.9)
- Conversões dinâmicas implícitas (§10.2.10)
- Conversões implícitas de parâmetros de tipo (§10.2.12)
- Conversões implícitas de expressões constantes (§10.2.11)
- Conversões implícitas definidas pelo utilizador (incluindo levantadas) (§10.2.14)
- Conversões de funções anónimas (§10.2.15)
- Conversões de grupos de métodos (§10.2.15)
- Conversões literais nulas (§10.2.7)
- Conversões implícitas anuláveis (§10.2.6)
- Conversões implícitas de tuplas (§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 membros de funções (§12.6.6), expressões de elenco (§12.9.8) e atribuições (§12.23).
As conversões implícitas pré-definidas sempre são bem-sucedidas e nunca fazem com que exceções sejam lançadas.
Nota: Conversões implícitas definidas pelo usuário adequadamente projetadas também devem exibir essas características. Nota final
Para efeitos de conversão, os tipos object e são convertíveis de identidade (dynamic).
No entanto, as conversões dinâmicas (§10.2.10) aplicam-se apenas a expressões de 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 que é equivalente em tempo de execução. Uma razão pela qual esta conversão existe é para que um tipo T ou uma expressão de tipo T possa ser considerado convertível para T si mesmo. Existem as seguintes conversões de identidade:
- Entre
TeT, para qualquer tipoT. - Entre
TeT?para qualquer tipo de referênciaT. - Entre
objectedynamic. - 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 onde 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,t2et3todos têm dois elementos: umintseguido por umstring. Os tipos de elementos tuplas podem ser feitos 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,t5et6.Exemplo final
Todas as conversões de identidade são simétricas. Se existir uma conversão de identidade de T₁ para T₂, então existe uma conversão de T₂ identidade de 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 tempo de execução. No entanto, uma vez que as operações de ponto flutuante podem ser realizadas com uma precisão superior à prescrita pelo seu tipo (§8.3.7), a atribuição dos seus resultados pode resultar numa perda de precisão, e é garantido que os moldes explícitos reduzem a precisão ao prescrito pelo tipo (§12.9.8).
10.2.3 Conversões numéricas implícitas
As conversões numéricas implícitas são:
- De
sbyte,shortint,long,float,double, oudecimal. - De
byte,shortushort, ,int,uint,long,ulongfloatdoubleou .decimal - De
short,intlong,float,double, oudecimal. - De
ushort,intuint,long, ,ulongfloat,double, oudecimal. - De
int, ,longfloatdouble, ou .decimal - De
uint,longulong,float,double, oudecimal. - De
long, ,floatdoubleoudecimal. - De
ulong, ,floatdoubleoudecimal. - De
char,ushortint, ,uint,long,ulongfloat,double, oudecimal. - De
floatadouble.
Conversões de int, uintou longulong para float e de long ou para ulong podem double 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.25) 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. Neste último caso, a conversão é avaliada convertendo para o enum_type subjacente e envolvendo o resultado (§8.3.12).
10.2.5 Conversões implícitas de cadeias interpoladas
Uma conversão de cadeia de caracteres interpolada implícita permite que um interpolated_string_expression (§12.8.3) seja convertido em ou System.IFormattable (que implementa System.FormattableString).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, é criada uma instância de System.FormattableString , conforme descrito mais detalhadamente no §12.8.3.
10.2.6 Conversões implícitas anuláveis
As conversões anuláveis implícitas são aquelas conversões anuláveis (§10.6.1) derivadas de conversões implícitas predefinidas.
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. Esta conversão produz 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 nulo dado.
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
objectedynamic. - De qualquer class_type
Sa qualquer class_typeT, desde queSderive deT. - De qualquer class_type
Sa qualquer , implementos fornecidosTS. - De qualquer interface_type
Sa qualquer , desde que seja derivadoTdeS. - De um com um tipo
Sde elemento para umSᵢcom um tipoTde elemento , desde que todos os itens a seguir sejam verdadeiros:-
SeTdiferem apenas no tipo de elemento. Por outras palavras,SeTtê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 deSparaT. - De qualquer array_type para
System.Arraye as interfaces que implementa. - De qualquer delegate_type para
System.Delegatee as interfaces que implementa. - Do literal nulo (§6.4.5.7) a qualquer tipo de referência.
- De qualquer
- De qualquer reference_type para uma interface ou tipo
Tde delegação, se tiver uma identidade implícita ou conversão de referência para uma interface ou tipoT₀de delegação eT₀for conversível em variação (§19.2.3.3) paraT. - Conversões implícitas envolvendo parâmetros de tipo que são conhecidos por serem tipos de referência. Ver §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 comprovadamente 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.
Nota: 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 que 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 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
- De qualquer non_nullable_value_type para qualquer interface_type
Ital que haja uma conversão de boxe do non_nullable_value_type para outro interface_typeI₀, eI₀seja conversível em variação (§19.2.3.3) paraI. - De qualquer nullable_value_type para qualquer reference_type onde haja 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 se sabe ser um tipo de referência para qualquer tipo tal que a conversão é permitida pelo §10.2.12.
Boxar 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.
Boxar um valor de um nullable_value_type produz uma referência nula se for o valor nulo (HasValue is false) ou o resultado de desempacotar e encaixotar o valor subjacente de outra forma.
Nota: O processo de boxe pode ser imaginado em termos da existência de uma classe de boxe para cada tipo de valor. Por exemplo, considere implementar
struct Suma 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(); } }Boxar um valor
vdo tipoSagora 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. Em vez disso, um valor in a box do tipo
Stem o tipoSde tempo de execução , e uma verificação de tipo de tempo de execução usando oisoperador 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 intUma 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 tipoobjectmenos 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 boxe implícita que ocorre na atribuição de
pparaboxfaz com que o valor depseja copiado. Se tivessePointsido declarado umclassem vez disso, o valor 20 seria saída porquepeboxfaria 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 de o boxe ser implementado 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 de tipo dinâmico para qualquer tipo T. A conversão é dinamicamente vinculada § 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 em tempo de execução será lançada.
Esta 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 constatação da conversão que causa a exceção. O risco de exceções em tempo de execução é inerente ao uso da vinculação dinâmica. Se a ligação dinâmica da conversão não for desejada, a expressão pode ser primeiro convertida para object, e depois para o tipo desejado.
Exemplo: O seguinte ilustra 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 existsAs atribuições para
s2eiambas empregam conversões dinâmicas implícitas, onde a vinculação das operações é suspensa até o tempo de execução. Em tempo de execução, conversões implícitas são procuradas do tipo de tempo de execução de (d) para o tipo destringdestino. Uma conversão é encontrada parastring, mas não paraint.Exemplo final
10.2.11 Conversões implícitas de expressões constantes
Uma conversão de expressão constante implícita permite as seguintes conversões:
- Um constant_expression (§12.25) do tipo
intpode ser convertido em tiposbyte,byte, ,shortushort,uint, ou , desdeulongque o valor do constant_expression esteja dentro do intervalo do tipo de destino. - Uma constant_expression de tipo pode ser convertida em tipo
long, desde que oulongnão seja negativo.
10.2.12 Conversões implícitas envolvendo parâmetros de tipo
Para um type_parameterT 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
Tpara sua classeCbase efetiva, deTqualquer classe base de , e deCqualquer interface implementadaTpelaC. - De
Tum interface_typeInoTconjunto de interfaces efetivo do e deTqualquer interface base do .I - De para um parâmetro de
Ttipo, desde queUdependa deT(U).Nota: Uma vez que
Té conhecido por ser um tipo de referência, dentro do âmbito doT, o tipo de tempo de execução de será sempre um tipo de referência, mesmo queUnão seja conhecido por ser um tipo deUreferência em tempo de compilação. Nota final - Do literal nulo (§6.4.5.7) a T.
Para um que nãoTconhecido por ser um tipo de referência §15.2.5, as seguintes conversões envolvendo são consideradas conversões de boxe (T) em tempo de compilação. Em tempo de execução, se T for um tipo de valor, a conversão é executada como uma conversão de boxe. Em tempo de execução, se T for um tipo de referência, a conversão é executada como uma conversão de referência implícita ou conversão de identidade.
- De
Tpara sua classeCbase efetiva, deTqualquer classe base de , e deCqualquer interface implementadaTpelaC.Nota:
Cserá um dos tiposSystem.Object,System.ValueTypeouSystem.Enum(caso contrárioTseria conhecido por ser um tipo de referência). Nota final - De
Tum interface_typeInoTconjunto de interfaces efetivo do e deTqualquer interface base do .I
Para um que nãoTconhecido por ser um tipo de referência, há uma conversão implícita de para um parâmetro de T tipo fornecido U depende de T. Em tempo de execução, se T for um tipo de valor e U for um tipo de referência, a conversão será executada como uma conversão de boxe. Em tempo de execução, se ambos T e U são tipos de valor, então T e U são necessariamente o mesmo tipo e nenhuma conversão é executada. Em tempo de execução, se T é um tipo de referência, então U é necessariamente também 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).
Existem as seguintes conversões implícitas adicionais para um determinado parâmetro Tde tipo:
- De para um tipo de
Treferência se tiver uma conversão implícita para um tipoSde 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
Tinterface, se tiver uma conversão implícita para um tipoIde interface, eI₀for convertível emI₀variância (IEm tempo de execução, seTfor um tipo de valor, a conversão é executada como uma conversão de boxe. Caso contrário, a conversão é 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 de boxe 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 implícitas de tuplas
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 T que 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 T de System.ValueTuple<...>', e inicializando cada um de seus campos em ordem da esquerda para a direita, avaliando a expressão correspondente do elemento de tupla de E, convertendo-o para o tipo de elemento correspondente usando T a conversão implícita encontrada, e inicializando o campo com o resultado.
Se o nome de um elemento na expressão da tupla não corresponder a um nome de elemento correspondente no tipo de tupla, deve ser emitido um aviso.
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 ignoredAs declarações de ,
t1t2et4são todas válidas, uma vez que existem conversões implícitas das expressões de elemento para os tipos det5elementos correspondentes. A declaração det3é inválida, porque não há conversão denullparaint. A declaração de causa um aviso porque os nomes dos elementos na expressão da tupla diferem daqueles no tipo det5tupla.Exemplo final
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 no §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, mas podem ser implicitamente convertidos em tipos delegados. Além disso, algumas expressões lambda podem ser implicitamente convertidas em tipos de árvore de expressão. As conversões de funções anónimas são descritas mais pormenorizadamente no §10.7 e as conversões de grupo de métodos no §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. Esta conversão produz o valor por defeito (§9.3) do tipo inferido.
10.2.17 Conversões de lançamento implícitas
Embora as expressões de lançamento não tenham um tipo, elas podem ser implicitamente convertidas em qualquer tipo.
10.2.18 Conversão de expressão de switch
Há uma conversão implícita de um switch_expression (§12.11) para cada tipo T para o qual existe uma conversão implícita de cada switch_expression_armswitch_expression_arm_expression para T.
10.3 Conversões explícitas
10.3.1 Generalidades
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 explícitas de tuplas (§10.3.6)
- Conversões de referência explícitas (§10.3.5)
- Conversões explícitas de interface
- Conversões de unboxing (§10.3.7)
- Conversões explícitas de parâmetros de tipo (§10.3.8)
- Conversões explícitas definidas pelo utilizador (§10.3.9)
Conversões explícitas podem ocorrer em expressões de elenco (§12.9.8).
O conjunto de conversões explícitas inclui todas as conversões implícitas.
Nota: Isso, por exemplo, permite que um cast explícito seja usado quando existe uma conversão de identidade implícita, a fim de forçar a seleção de uma sobrecarga de método particular. 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 são conhecidas por possivelmente perder 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,byteushort,uint,ulong, ouchar. - De
byteousbytecharpara . - De
short,sbytebyte,ushort,uint,ulong, ouchar. - De
ushort, ,sbytebyteshort, ou .char - De
int,sbytebyte,short, ,ushortuint,ulong, ouchar. - De
uint,sbytebyte,short,ushort,int, ouchar. - De
long,sbytebyte, ,short,ushort,intuint,ulong, ouchar. - De
ulong,sbytebyte, ,short,ushort,intuint,long, ouchar. - De
char, ,sbytebyteoushort. - De
float,sbytebyte, ,short,ushort,intuint,long,ulongchar, , oudecimal. - De
double,sbytebyte,short, ,ushort,intuint,long,ulong,charfloat, , oudecimal. - De
decimal,sbytebyte,short, ,ushort,intuint,long,ulong,charfloat, , oudouble.
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 outra numeric_type usando uma expressão de elenco (§12.9.8).
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 forma:
- 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) em que a conversão ocorre:
- Em um
checkedcontexto, 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.OverflowExceptionse o valor do operando de origem estiver fora do intervalo do tipo de destino. - Em um
uncheckedcontexto, a conversão sempre é bem-sucedida e prossegue da seguinte forma.- Se o tipo de origem for maior que o tipo de destino, o valor de origem será truncado descartando seus bits "extra" mais significativos. O resultado é então tratado como um valor do tipo de destino.
- Se o tipo de origem tiver o 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 um tipo integral, o valor de
decimalorigem é arredondado para zero para o valor integral mais próximo, e esse valor integral torna-se o resultado da conversão. Se o valor integral resultante estiver fora do intervalo do tipo de destino, umSystem.OverflowExceptionserá lançado. - Para uma conversão de ou
floatpara um tipo integral, o processamento depende do contexto de verificação dedoubleestouro (§12.8.20) em que a conversão ocorre:- Num contexto verificado, a conversão procede da seguinte forma:
- Se o valor do operando for NaN ou infinito, a
System.OverflowExceptioné lançado. - Caso contrário, o operando de origem é 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, um
System.OverflowExceptioné jogado.
- 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 é 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 é um valor não especificado do tipo de destino.
- Num contexto verificado, a conversão procede da seguinte forma:
- Para uma conversão de
doubleparafloat, odoublevalor é arredondado para o valor mais próximofloat. Se odoublevalor for muito pequeno para ser representado como umfloat, o resultado se tornará zero com o mesmo sinal que o valor. Se a magnitude dodoublevalor for muito grande para representar como umfloat, o resultado torna-se infinito com o mesmo sinal que o valor. Se odoublevalor for NaN, o resultado também será NaN. - Para uma conversão de ou
floatparadouble, o valor dedecimalorigem é convertido emdecimalrepresentaçã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 sedecimaloferecer suporte a valores zero assinados. - Se a magnitude do valor de origem for muito grande para representar como um
decimal, ou esse valor for infinito, o resultado será infinito preservando o sinal do valor original, se a representação decimal suportar infinidades, caso contrário, um System.OverflowException será lançado. - Se o valor de origem for NaN, o resultado será NaN se a representação decimal suportar NaNs; caso contrário, um System.OverflowException será lançado.
- Se o valor de origem for muito pequeno para ser representado como um
- Para uma conversão de
decimalparafloatoudouble, odecimalvalor é arredondado para o valor ou mais próximodoublefloat. 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 a precisão, ela nunca faz com que uma exceção seja lançada.
Nota: O
decimaltipo não é necessário para suportar infinidades ou valores de NaN, mas pode fazê-lo, o seu intervalo pode ser menor do que o intervalo defloatedouble, mas não é garantido que seja. Paradecimalrepresentações sem infinidades ou valores NaN, e com um intervalo menor quefloat, o resultado de uma conversão dedecimalpara um oufloatdoublenunca será infinito ou NaN. Nota final
10.3.3 Conversões de enumeração explícitas
As conversões de enumeração explícitas são:
- De
sbyte,byte,short, ,ushortint,uintlongulongcharfloatdouble, oudecimala qualquer enum_type. - De qualquer enum_type para
sbyte,byte, ,shortushort,int,uint,long,ulongcharfloatdoubleou .decimal - 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: Dado um enum_type
Ecom um tipo subjacente deint, uma conversão deEparabyteé processada como uma conversão numérica explícita (§10.3.2) deintparabyte, e uma conversão debyteparaEé processada como uma conversão numérica implícita (§10.2.3) debyteparaint. Exemplo final
10.3.4 Conversões anuláveis explícitas
As conversões anuláveis explícitas são aquelas conversões anuláveis (§10.6.1) derivadas de conversões explícitas e implícitas predefinidas.
10.3.5 Conversões de referência explícitas
As conversões de referência explícitas são:
- Do objeto a qualquer outro reference_type.
- De qualquer class_type
Sa qualquer class_typeT, desdeSque seja uma classe base deT. - De qualquer class_type
Sa qualquer interface_typeT, desde queSnão seja selado e desde queSnão implementeT. - De qualquer interface_type
Sa qualquer , desde queTnão sejam selados ou fornecidosTimplementosT. - De qualquer interface_type
Sa qualquer interface_typeT, desde queSnão derive deT. - De um com um tipo
Sde elemento para umSᵢcom um tipoTde elemento , desde que todos os itens a seguir sejam verdadeiros:-
SeTdiferem apenas no tipo de elemento. Por outras palavras,SeTtêm o mesmo número de dimensões. - Existe uma conversão de referência explícita de
SᵢparaTᵢ.
-
- De
System.Arraye as interfaces que implementa, para qualquer array_type. - De um array_type
S[]paraSystem.Collections.Generic.IList<T>,System.Collections.Generic.IReadOnlyList<T>e suas interfaces de base, desde que haja uma conversão de identidade ou conversão de referência explícita deSparaT. - De
System.Collections.Generic.IList<S>,System.Collections.Generic.IReadOnlyList<S>, e suas interfaces de base para um tipoT[]de matriz unidimensional , desde que haja uma conversão de identidade ou conversão de referência explícita deSpara T. - De
System.Delegatee as interfaces que implementa para qualquer delegate_type. - De um tipo
Sde referência para um tipoTde referência se ele tiver uma conversão de referência explícita de para um tipoSdeT₀referência eT₀houver uma conversão de identidade deT₀paraT. - De um tipo
Sde referência para uma interface ou tipoTde delegação, se houver uma conversão de referência explícita de para uma interface ou tipoSdeT₀delegado eT₀for conversível emTvariância ou for conversível em variação paraT§T₀19.2.3.3. - De
D<S₁...Sᵥ>ondeD<T₁...Tᵥ>D<X₁...Xᵥ>é 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 dasDseguintes retenções:- Se
Xᵢé invariante, entãoSᵢé idêntico aTᵢ. - Se
Xᵢfor covariante, então há 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 em 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 possa ser convertido para o 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, um System.InvalidCastException é lançado.
Nota: As conversões de referência, implícitas ou explícitas, nunca alteram o valor da própria referência (§8.2.1), apenas o seu tipo, nem o tipo ou valor do objeto que está a ser referenciado. Nota final
10.3.6 Conversões explícitas de tuplas
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 T de System.ValueTuple<...>, e inicializando cada um de seus campos em ordem da esquerda para a direita, avaliando a expressão correspondente do elemento de tupla de E, convertendo-o 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 unboxing permite que um reference_type seja explicitamente convertido em value_type. Existem as seguintes conversões de unboxing:
- Do tipo
objecta qualquer value_type. - Do tipo
System.ValueTypea qualquer value_type. - Do tipo
System.Enuma qualquer enum_type. - De qualquer interface_type a qualquer non_nullable_value_type que implemente o interface_type.
- De qualquer para qualquer
Ionde há uma conversão de unboxing de um interface_type para oI₀non_nullable_value e uma conversão de identidade de paraI. - De qualquer interface_type
Ipara qualquer non_nullable_value_type em que haja uma conversão de unboxing de um interface_typeI₀para o non_nullable_value_type eI₀seja variance_convertible ouIIseja conversível emI₀variação (§19.2.3.3). - De qualquer reference_type para qualquer nullable_value_type onde 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 se sabe ser um tipo de valor para qualquer tipo tal que a conversão é permitida pelo §10.3.8.
Uma operação de unboxing para um non_nullable_value_type consiste em primeiro verificar se a ocorrência do objeto é um valor em caixa do non_nullable_value_type determinado e, em seguida, copiar o valor para fora da instância.
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 do unboxing da instância de objeto para o tipo subjacente do nullable_value_type caso contrário.
Nota: Referindo-se à classe de boxe imaginária descrita no §10.2.9, uma conversão de unboxing de uma caixa de objeto para uma value_type
Sconsiste em executar a expressão((S_Boxing)box).value. Assim, as declaraçõesobject box = new S(); S s = (S)box;correspondem conceptualmente a
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;Nota final
Para que uma conversão de unboxing para 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 in a box desse non_nullable_value_type. Se o operando de origem for null um System.NullReferenceException é lançado. Se o operando de origem for uma referência a um objeto incompatível, um System.InvalidCastException será lançado.
Para que uma conversão de unboxing para 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 in a box do non_nullable_value_type subjacente do nullable_value_type. Se o operando de origem for uma referência a um objeto incompatível, um System.InvalidCastException será lançado.
10.3.8 Conversões explícitas envolvendo parâmetros de tipo
Para um type_parameterT 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
Cbase efetiva deTparaTe de qualquer classe base deCparaT. - De qualquer interface_type a
T. - De
Tpara qualquer interface_typeIdesde que ainda não haja uma conversão de referência implícita deTparaI. - De um para
Udesde queTdependa (TU).Nota: Como
Té conhecido por ser um tipo de referência, dentro do escopo doT, o tipo de tempo de execução de você sempre será um tipo de referência, mesmo queUnão seja conhecido por ser um tipo de referência em tempo de compilação. Nota final
Para um type_parameterT 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 unboxing (§10.3.7) em tempo de compilação. Em tempo de execução, se T for um tipo de valor, a conversão é executada como uma conversão de unboxing. Em tempo de execução, se T for 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
Cbase efetiva deTparaTe de qualquer classe base deCparaT.Nota: C será um dos tipos
System.Object,System.ValueTypeouSystem.Enum(caso contrárioTseria conhecido por ser um tipo de referência). Nota final - De qualquer interface_type a
T.
Para um que nãoTconhecido por ser um tipo de referência (§15.2.5), existem as seguintes conversões explícitas:
- De
Tpara qualquer interface_typeIdesde que não haja já uma conversão implícita deTparaI. Esta conversão consiste numa conversão de boxe implícita (§10.2.9) deTpara seguidaobjectde uma conversão de referência explícita deobjectparaI. Em tempo de execução, seTfor um tipo de valor, a conversão é executada como uma conversão de boxe seguida por uma conversão de referência explícita. Em tempo de execução, seTfor um tipo de referência, a conversão é executada como uma conversão de referência explícita. - De um parâmetro
Ude tipo paraTdesde queTdependa deU(§15.2.5). Em tempo de execução, seTfor um tipo de valor eUfor um tipo de referência, a conversão será executada como uma conversão de unboxing. Em tempo de execução, se ambosTeUsão tipos de valor, entãoTeUsão necessariamente o mesmo tipo e nenhuma conversão é executada. Em tempo de execução, seTé um tipo de referência, entãoUé necessariamente também 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 sem restrições para um tipo sem interface, o que pode ser surpreendente. A razão para esta regra é evitar confusões 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
temlongfosse permitida, seria de esperar que issoX<int>.F(7)voltasse7L. No entanto, não o faria, porque as conversões numéricas padrão só são consideradas quando se sabe que os tipos são numéricos em tempo de vinculação. A fim de 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çaria uma exceção em tempo de execução, já que um box nãointpode ser convertido diretamente em umlongarquivo .Exemplo final
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ícita ou explícita definida pelo usuário, seguida 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 no §10.5.5.
10.4 Conversões padrão
10.4.1 Generalidades
As conversões padrão são aquelas conversões 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 boxe (§10.2.9)
- Conversões implícitas de expressões constantes (§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.
Nota: Em outras palavras, se existe uma conversão implícita padrão de um tipo
Apara um tipoB, então existe uma conversão explícita padrão de tipoApara tipoBe de tipoBpara tipoA. Nota final
10.5 Conversões definidas pelo usuário
10.5.1 Generalidades
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 Tde destino , se S ou T são tipos de valor anuláveis, deixe S₀ e T₀ faça referência aos 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. - Ou
S₀T₀é o tipo de classe ou estrutura em que a declaração do operador tem lugar. - Nem
S₀T₀é uma interface_type. - Excluindo conversões definidas pelo usuário, não existe uma conversão de
SparaTou deTparaS.
As restrições que se aplicam às conversões definidas pelo usuário são especificadas no §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 um centro de conversão definido pelo usuário para encontrar o operador de conversão mais específico definido pelo usuário para a expressão de origem e o tipo de destino. Esta determinação divide-se em várias etapas:
- Encontrar o conjunto de classes e estruturas a partir das quais 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 este efeito, assume-se que apenas classes e structs podem declarar operadores definidos pelo usuário e que tipos não-classe não têm classes base. Além disso, se o tipo de origem ou destino for um tipo de valor anulável, seu 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 efetuar uma conversão normalizada (ponto 10.4) da expressão de origem para o tipo de operando do operador, e deve ser possível efetuar uma conversão normalizada do tipo de resultado do operador para o tipo alvo.
- A partir do conjunto de operadores aplicáveis definidos pelo usuário, 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 preferidos em relação aos operadores de conversão levantados. As regras exatas para estabelecer o operador de conversão mais específico definido pelo usuário são definidas nas subcláusulas a seguir.
Uma vez identificado um operador de conversão definido pelo usuário mais específico, 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, chamando o operador de conversão definido pelo usuário ou levantado para executar a conversão.
- Finalmente, 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 primeiro executará 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 das conversões implícitas ou explícitas definidas pelo utilizador são dadas nas subcláusulas seguintes. As definições utilizam os seguintes termos:
- Se uma conversão implícita padrão (§10.4.2) existe de um tipo
Apara um tipoB, e se nemAnemBsão interface_types, entãoAé dito ser englobado porB, e diz-se queBenglobaA. - Se uma conversão implícita padrão (§10.4.2) existe de uma expressão
Epara um tipoB, e se nemBnem o tipo deE(se tiver um) são interface_types, então diz-se queEé englobado porB, e diz-se queBenglobaE. - O tipo mais abrangente em um conjunto de tipos é o tipo que engloba todos os outros tipos no conjunto. Se nenhum tipo engloba todos os outros tipos, então o conjunto não tem o tipo mais abrangente. Em termos mais intuitivos, o tipo mais abrangente é o tipo "maior" do conjunto — aquele tipo para o qual cada um dos outros tipos pode ser implicitamente convertido.
- O tipo mais englobado em um conjunto de tipos é o tipo que é englobado por todos os outros tipos no conjunto. Se nenhum tipo único é englobado por todos os outros tipos, então o conjunto não tem nenhum tipo mais englobado. Em termos mais intuitivos, o tipo mais englobado é o tipo "menor" do conjunto — aquele tipo que pode ser implicitamente convertido para 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
SeS₀T₀.- Se
Etem um tipo, que sejaSesse tipo. - Se
SouTsão tipos de valor anuláveis, deixeSᵢeTᵢsejam seus tipos subjacentes, caso contrário, letSᵢeTᵢbeSeT, respectivamente. - Se
SᵢouTᵢsão parâmetros de tipo, deixeS₀eT₀sejam suas classes base efetivas, caso contrário, deixeS₀eT₀sejaSᵢeTᵢ, respectivamente.
- Se
Encontre o conjunto de tipos,
D, a partir dos quais os operadores de conversão definidos pelo usuário serão considerados. Este conjunto consiste emS₀(seS₀existe e é uma classe ou struct), as classes base deS₀(seS₀existe e é uma classe) eT₀(seT₀é uma classe ou struct). Um tipo é adicionado ao conjuntoDsomente se uma conversão de identidade para outro tipo já incluído no conjunto não existir.Encontre 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 emDque convertem de um tipo abrangente para um tipo englobadoEporT. SeUestiver vazia, a conversão é indefinida e ocorre um erro em tempo de compilação.Encontre o tipo de fonte mais específico,
Sₓ, dos operadores emU:- Se
Sexiste e qualquer um dos operadores emUconverter deS, 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 abrangente não puder ser encontrado, então a conversão é ambígua e ocorre um erro em tempo de compilação.
- Se
Encontre o tipo de alvo mais específico,
Tₓ, dos operadores emU:- Se qualquer um dos operadores em
Uconverter paraT, entãoTₓéT. - Caso contrário,
Tₓé o tipo mais abrangente no conjunto combinado de tipos de destino dos operadores emU. Se exatamente um tipo mais abrangente não puder ser encontrado, então a conversão é ambígua e ocorre um erro em tempo de compilação.
- Se qualquer um dos operadores em
Encontre o operador de conversão mais específico:
- Se
Ucontiver exatamente um operador de conversão definido pelo usuário que converte de paraSₓ, então este é o operador deTₓconversão mais específico. - Caso contrário, se
Ucontiver exatamente um operador de conversão levantado que converte de paraSₓ, então este é o operador deTₓconversão mais específico. - Caso contrário, a conversão é ambígua e ocorre um erro em tempo de compilação.
- Se
Por fim, aplique a conversão:
- Se E ainda não tiver o tipo
Sₓ, então uma conversão implícita padrão deEparaSₓé executada. - O operador de conversão mais específico é invocado para converter de
SₓparaTₓ. - Se
TₓnãoTfor , então uma conversão implícita padrão deTₓparaTé realizada.
- 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 de 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
SeS₀T₀.- Se
Etem um tipo, que sejaSesse tipo. - Se
SouTsão tipos de valor anuláveis, deixeSᵢeTᵢsejam seus tipos subjacentes, caso contrário, letSᵢeTᵢbeSeT, respectivamente. - Se
SᵢouTᵢsão parâmetros de tipo, deixeS₀eT₀sejam suas classes base efetivas, caso contrário, deixeS₀eT₀sejaSᵢeTᵢ, respectivamente.
- Se
- Encontre o conjunto de tipos,
D, a partir dos quais os operadores de conversão definidos pelo usuário serão considerados. Este conjunto consiste emS₀(seS₀existe e é uma classe ou struct), as classes base deS₀(seS₀existe e é uma classe),T₀(seT₀é uma classe ou struct) e as classes base deT₀(seT₀é uma classe). Um tipo é adicionado ao conjuntoDsomente se uma conversão de identidade para outro tipo já incluído no conjunto não existir. - Encontre o conjunto de operadores de conversão definidos pelo usuário e levantados aplicáveis,
U. Este conjunto consiste nos operadores de conversão implícitos ou explícitos definidos pelo utilizador e levantados declarados pelas classes ou estruturas emDque convertem de um tipo que englobaEou engloba porS(se existir) para um tipo que engloba ou engloba porT. SeUestiver vazia, a conversão é indefinida e ocorre 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
Uconverter deS, entãoSₓéS. - Caso contrário, se qualquer um dos operadores em
Uconverter de tipos que englobamE, entãoSₓé o tipo mais englobado no conjunto combinado de tipos de origem desses operadores. Se nenhum tipo mais abrangente 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 emU. Se exatamente um tipo mais abrangente não puder ser encontrado, então a conversão é ambígua e ocorre um erro em tempo de compilação.
- Se S existe e qualquer um dos operadores em
- Encontre o tipo de alvo mais específico,
Tₓ, dos operadores emU:- Se qualquer um dos operadores em
Uconverter paraT, entãoTₓéT. - Caso contrário, se qualquer um dos operadores em
Uconverter para 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, então a conversão é ambígua e ocorre um erro em tempo de compilação. - Caso contrário,
Tₓé o tipo mais englobado no conjunto combinado de tipos de destino dos operadores emU. Se nenhum tipo mais abrangente 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 contém exatamente um operador de conversão definido pelo usuário que converte de para
Sₓ, então este é o operador deTₓconversão mais específico. - Caso contrário, se
Ucontiver exatamente um operador de conversão levantado que converte de paraSₓ, então este é o operador deTₓconversão mais específico. - Caso contrário, a conversão é ambígua e ocorre um erro em tempo de compilação.
- Se U contém exatamente um operador de conversão definido pelo usuário que converte de para
- Por fim, aplique a conversão:
- Se
Eainda não tiver o tipoSₓ, então uma conversão explícita padrão de E paraSₓé executada. - O operador de conversão mais específico definido pelo usuário é invocado para converter de
SₓparaTₓ. - Se
TₓnãoTfor , então uma conversão explícita padrão deTₓparaTé executada.
- Se
Existe uma conversão explícita definida pelo usuário de um tipo S para um tipo T se existir uma conversão explícita definida pelo usuário de uma variável de tipo S para T.
10.6 Conversões envolvendo tipos anuláveis
10.6.1 Conversões anuláveis
Uma conversão anulável permite que uma conversão predefinida que opera em um tipo de valor não anulável também seja usada com a forma anulável desse tipo. 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
SparaT? - Uma conversão explícita de
S?paraT.
Uma conversão anulável é classificada como uma conversão implícita ou explícita.
Certas 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 satisfazem os requisitos do §10.4.3 são classificadas como conversões explícitas padrão.
Avaliação de uma conversão anulável com base numa conversão subjacente do ST produto do seguinte modo:
- Se a conversão anulável for de
S?paraT?:- Se o valor de origem for null (
HasValuepropriedade forfalse), o resultado será o valor nulo do tipoT?. - Caso contrário, a conversão é avaliada como um desempacotamento de
S?paraS, seguido pela conversão subjacente deSparaT, seguido por um encapsulamento deTparaT?.
- Se o valor de origem for null (
- Se a conversão anulável for de
SparaT?, a conversão é avaliada como a conversão subjacente deSparaTseguida de um wrapping deTparaT?. - Se a conversão anulável for de
S?paraT, a conversão é avaliada como um desempacotamento deS?paraSseguido pela conversão subjacente deSparaT.
10.6.2 Conversões suspensas
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 Tde valor não anulável, existe um operador de conversão suspenso que converte de S? para T?. Esse operador de conversão levantado executa um desempacotamento de S? para seguido pela conversão definida pelo usuário de S para S seguido por um encapsulamento de T para T, exceto que um valor T? nulo converte diretamente em um valor S?nulo T? . Um operador de conversão levantado tem a mesma classificação implícita ou explícita que o seu operador de conversão subjacente definido pelo utilizador.
10.7 Conversões de funções anónimas
10.7.1 Generalidades
Uma anonymous_method_expression ou lambda_expression é classificada como uma função anónima (§12.21). A expressão não tem um tipo, mas pode ser implicitamente convertida em um tipo de delegado compatível. Algumas expressões lambda também podem ser implicitamente convertidas 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, desde que:
- Se
Fcontém um anonymous_function_signature, entãoDeFtem o mesmo número de parâmetros. - Se
Fnão contém um anonymous_function_signature, então pode ter zero ou mais parâmetros de qualquer tipo, desde queDnenhum parâmetro de é um parâmetro deDsaída. - Se
Ftiver uma lista de parâmetros explicitamente tipada, cada parâmetro emDterá os mesmos modificadores que o parâmetro correspondente emFe existe uma conversão de identidade entre o parâmetro correspondente emF. - Se
Ftem uma lista de parâmetros digitada implicitamente,Dnão tem parâmetros de referência ou saída. - Se o corpo de
Fé uma expressão, e temDum tipo de retorno vazio ouFé assíncrono eDtem um«TaskType»tipo de retorno (§15.14.1), então quando cada parâmetro deFrecebe o tipo do parâmetro correspondente emD, o corpo deFé uma expressão válida (em relação a §12) que será permitido como uma expressão_de_instrução (§13.7). - Se o corpo de é um bloco, e
Fum tipo deDretorno vazio ouFé assíncrono eDtem um«TaskType»tipo de retorno, então quando cada parâmetro deFé dado o tipo do parâmetro correspondente emD, o corpo deFé um bloco válido (w.r.t §13.3) no qual nenhumareturninstrução especifica uma expressão. - Se o corpo de
Fé uma expressão, e querFé não-assíncrono eDtem um tipo de retornovoidnão-T, ouFé assíncrono eDtem um«TaskType»<T>tipo de retorno (§15.14.1), então quando cada parâmetro deFrecebe o tipo do parâmetro correspondente emD, o corpo deFé uma expressão válida (w.r.t §12) que é implicitamente conversível emT. - Se o corpo de
Fé um bloco, e ouFé não-assíncrono eDtem um tipoTde retorno não-void , ouFé assíncrono eDtem um«TaskType»<T>tipo de retorno, então quando cada parâmetro deFé dado o tipo do parâmetro correspondente emD, o corpo de é um bloco deFinstruçã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 emT.
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 final
Exemplo: Os exemplos a seguir usam um tipo
Func<A,R>de delegado genérico que representa uma função que usa um argumento de tipoAe retorna um valor de 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; // OkOs 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 para o tipo
Func<int,int>delegado porque, quandoxé dado tipoint,x + 1é uma expressão válida que é implicitamente conversível em tipoint.Da mesma forma, a segunda atribuição converte com êxito a função anônima para o tipo delegado Func<int,double> porque o resultado de
x + 1(do tipoint) é implicitamente conversível em tipodouble.No entanto, a terceira atribuição é um erro em tempo de compilação porque, quando
xé dado tipodouble, o resultado dex + 1(do tipodouble) não é implicitamente convertível em tipoint.A quarta atribuição converte com êxito a função assíncrona anônima para o tipo
Func<int, Task<int>>delegado porque o resultado de (do tipox + 1) é implicitamente conversível para o tipointde retorno efetivo do lambda assíncrono, que tem um tipo deintretornoTask<int>.Exemplo final
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 Dde 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. Ver §12.6 para mais pormenores.
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 para um tipo de 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.5) 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 produzida 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 de inclusão ou algum outro objeto.
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 delegados são permitidas (mas não obrigatórias) para retornar a mesma instância delegada. O termo semanticamente idêntico é usado aqui para significar que a execução das funções anônimas produzirá, em todos os casos, os mesmos efeitos dados os mesmos argumentos. Esta 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ônimos têm o mesmo conjunto (vazio) de variáveis externas capturadas, e como as funções anônimas são semanticamente idênticas, um compilador pode fazer com que os delegados se refiram ao mesmo método de destino. Na verdade, um 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ões 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 de delegado compatível sempre existe, mas pode falhar em tempo de compilação por motivos definidos pela implementação.
Nota: 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
asyncmodificador- 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 dinamicamente ligada
Nota final
10.8 Conversões de grupo de métodos
Existe uma conversão implícita de um grupo de métodos (§12.2) para um tipo de delegado compatível (§21.4). Se D é um tipo delegado, e E é uma expressão que é classificada como um grupo de métodos, então D é compatível com E se e somente se E contém pelo menos um método que é 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âmetros 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 de delegado D é descrita a seguir.
- É selecionado um único método
Mcorrespondente a uma invocação de método (§12.8.10.2) do formulárioE(A), com as seguintes modificações:- A lista
Ade 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 na parameter_list deD— exceto parâmetros do tipodynamic, onde a expressão correspondente tem o tipoobjectem vez dedynamic. - Os métodos candidatos considerados são apenas os que são aplicáveis na sua forma normal e não omitem quaisquer parâmetros opcionais (§12.6.4.2). Assim, os métodos candidatos são ignorados se forem aplicáveis apenas na sua forma expandida ou se um ou mais dos seus parâmetros opcionais não tiverem um parâmetro correspondente no
D.
- A lista
- Considera-se que existe uma conversão se o algoritmo do §12.8.10.2 produzir um único melhor método
Mque seja compatível (§21.4) comD. - Se o método
Mselecionado for um método de instância, a expressão de instância associada aEdeterminará o objeto de destino do delegado. - Se o método
Mselecionado for um método de extensão que é 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étodo:
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
d1converte implicitamente o grupoFde métodos em um valor do tipoD1.A atribuição para
d2mostra como é possível criar um delegado para um método que tem menos tipos de parâmetros derivados (contravariantes) e um tipo de retorno mais derivado (covariante).A atribuição para
d3mostra como nenhuma conversão existe se o método não for aplicável.A atribuição para
d4mostra como o método deve ser aplicável em sua forma normal.A atribuição para
d5mostra como os tipos de parâmetro e retorno do delegado e do método podem diferir apenas para tipos de referência.Exemplo final
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, em vez disso, ser escrito
object obj = (EventHandler)myDialog.OkClick;Exemplo final
Uma conversão de grupo de métodos pode referir-se a um método genérico, especificando explicitamente argumentos de tipo dentro Ede , ou através de inferência de tipo (§12.6.3). Se a inferência de tipo for usada, os tipos de parâmetros 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; Estes 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 final
Os grupos de métodos podem influenciar a resolução da sobrecarga e participar na inferência de tipos. Ver §12.6 para mais pormenores.
A avaliação em tempo de execução de uma conversão de grupo de métodos prossegue da seguinte forma:
- 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 da instância é avaliada. Se essa avaliação causar uma exceção, nenhuma etapa adicional 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.NullReferenceExceptionserá lançado e nenhuma outra etapa será executada. - Se a expressão da instância for de um value_type, uma operação de boxe (§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 faz parte de uma chamada de método estático e o objeto de destino do delegado é
null. - Uma instância delegada do tipo
Ddelegado é 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 forma:- 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 tiver sido reutilizada, é criada uma nova instância (§21.5). Se não houver memória suficiente disponível para alocar a nova instância, um
System.OutOfMemoryExceptionserá lançado. Caso contrário, a instância é inicializada com as referências fornecidas.
ECMA C# draft specification