Desvios do padrão C#

Este documento lista inconsistências entre Roslyn e o padrão C# onde eles são conhecidos, organizados por seção padrão.

Conversions

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

Do §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 seja convertido em qualquer enum_type e em qualquer nullable_value_type cujo tipo subjacente seja um enum_type.

Roslyn executa conversões de enumeração implícitas 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 não são permitidas a partir, corretamente, de expressões constantes que tenham um tipo de , outras enumerações ou tipos de referência.

Consulta de membros

A partir do §12.5.1:

  • Finalmente, tendo removido membros ocultos, o resultado da pesquisa é determinado:
    • Se o conjunto consiste em um único membro que não é um método, então esse membro é 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 é ambígua e ocorre um erro de vinculação de tempo.

Em vez disso, Roslyn implementa uma preferência por métodos em vez de símbolos que não são métodos.

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 bem conhecidos

O compilador é livre para fazer suposições sobre a forma e o comportamento de tipos/membros bem conhecidos. Ele pode não verificar restrições inesperadas, Obsolete atributo ou UnmanagedCallersOnly atributo. Pode realizar algumas otimizações com base nas expectativas de que os tipos/membros são bem comportados. Nota: o compilador deve permanecer resiliente à falta de tipos/membros bem conhecidos.

Métodos de interface parciais

Os métodos parciais da interface são implicitamente não virtuais, ao contrário dos métodos de interface não parciais e outros tipos de membros parciais da interface, veja uma alteração disruptiva relacionada e LDM 2025-04-07.