Desvios do padrão C#

Este documento lista inconsistências entre Roslyn e o padrão C# em que elas são conhecidas, organizadas por seção padrão.

Conversions

Conversões implícitas de enum a partir de zero

A partir de §10.2.4:

Uma conversão de enumeração implícita permite que um constant_expression (§12,25) com qualquer tipo inteiro e o valor zero sejam convertidos em qualquer enum_type e em qualquer nullable_value_type cujo tipo subjacente seja um enum_type.

Roslyn executa conversões implícitas de enumeração a partir de expressões constantes com tipos de float, double e decimal também.

enum SampleEnum
{
    Zero = 0,
    One = 1
}

class EnumConversionTest
{
    const float ConstFloat = 0f;
    const double ConstDouble = 0d;
    const decimal ConstDecimal = 0m;

    static void PermittedConversions()
    {
        SampleEnum floatToEnum = ConstFloat;
        SampleEnum doubleToEnum = ConstDouble;
        SampleEnum decimalToEnum = ConstDecimal;
    }
}

Conversões (corretamente) não são permitidas de expressões constantes que têm um tipo de bool, outras enumerações ou tipos de referência.

Consulta de membro

A partir de §12.5.1:

  • Finalmente, tendo removido os membros ocultos, o resultado da pesquisa é determinado:
    • Se o conjunto consistir em um único membro que não seja um método, esse membro será o resultado da pesquisa.
    • Caso contrário, se o conjunto contiver apenas métodos, esse grupo de métodos será o resultado da pesquisa.
    • Caso contrário, a pesquisa será ambígua e ocorrerá um erro no momento da associação.

Roslyn, em vez disso, implementa uma preferência por métodos em relação aos símbolos não-metódicos.

var x = I.M; // binds to I1.M (method)
x();

System.Action y = I.M; // binds to I1.M (method)

interface I1 { static void M() { } }
interface I2 { static int M => 0;   }
interface I3 { static int M = 0;   }
interface I : I1, I2, I3 { }
I i = null;
var x = i.M; // binds to I1.M (method)
x();

System.Action y = i.M; // binds to I1.M (method)

interface I1 { void M() { } }
interface I2 { int M => 0;   }
interface I : I1, I2 { }

Suposições sobre tipos/membros conhecidos

O compilador é livre para fazer suposições sobre a forma e o comportamento de tipos/membros conhecidos. Ele pode não verificar se há restrições inesperadas, Obsolete atributo ou UnmanagedCallersOnly atributo. Ele pode executar algumas otimizações com base nas expectativas de que os tipos/membros sejam bem comportados. Observação: o compilador deve permanecer resiliente a tipos/membros conhecidos ausentes.

Métodos parciais de interface

Métodos parciais de interface são implicitamente não virtuais, diferentemente de métodos de interface não parciais e outros tipos de membros parciais de interface. Consulte uma modificação disruptiva relacionada e LDM 2025-04-07.