Compartir a través de


Cambios problemáticos en Visual C# 2008

Actualización: Julio de 2008

Cambios problemáticos en Visual C# 2008 Service Pack 1

En la tabla siguiente se hacen una lista de todos los cambios problemáticos en Visual C# 2008 Service Pack 1 que pueden afectar a las aplicaciones creadas en la versión de lanzamiento original de Visual C# 2008 o en Visual C# 2005.

Número de cambio

Category

Problema

Descripción

1

Resolución de sobrecarga

La inferencia de tipos está ahora incluida en las matrices de tipos de puntero en la resolución de sobrecarga de los métodos.

En Visual C# 2008 y versiones anteriores, la inferencia de tipos provoca la exclusión de las matrices de tipos de puntero del proceso de resolución de sobrecarga de los métodos. En el código siguiente, el compilador de Visual C# 2005 selecciona la versión no genérica de Test porque la versión genérica de Test no se tiene en cuenta debido a su parámetro de tipo int*[]. En Visual C# 2008, se selecciona la versión genérica de Test.

using System.Collections.Generic;
unsafe class Program
{
    static void Main()
    {
        IEnumerable<int*[]> y = null;
        Test(y); 
    }
// Selected by Visual C# 2008.
    static void Test<S>(IEnumerable<S> x) { } // Selected by Visual C# 2005.
    static void Test(object o) { } 
}

2

Indizadores

Ahora el compilador genera el error CS0466 para los indizadores y las propiedades además de generarlo para los métodos.

En la versión de lanzamiento original de Visual C# 2008 y versiones anteriores, es posible definir una implementación explícita de un indizador en el que la implementación tiene un parámetro params pero la definición de interfaz no. Esta construcción es contraria a la especificación. En Visual C# 2008 SP1, esta construcción genera el Error del compilador CS0466, como se muestra en el código siguiente.

interface I
{
    int this[int[] p] { set; }
}
class Base : I
{
// Produces CS0466:
    int I.this[params int[] p]    {
        set
        {
        }
    }

}

3

Tipos que aceptan valores NULL y expresiones ??

El compilador evalúa ahora correctamente las expresiones en las que las variables que aceptan valores NULL se comparan consigo mismas.

En la versión de lanzamiento original de Visual C# 2008, el código siguiente compila y genera "false" en tiempo de ejecución. En Visual C# 2008 Service Pack 1, se genera la Advertencia del compilador (nivel 3) CS1718 y se produce "true" como resultado.

static class Program
{
    static void Main()
    {
        int? x = null;
        bool y = x == x;
        Console.WriteLine(y);
    }
}

4

try-finally en iteradores

Ha cambiado la ejecución de bloques finally anidados de los iteradores con instrucciones break.

En la versión de lanzamiento original de Visual C# 2008, el código siguiente ejecuta el bloque finally exterior dos veces. En Visual C# 2008 SP1, el bloque finally exterior se ejecuta una vez.

using System;
using System.Collections;
using System.Collections.Generic;
public class Test
{
    public static void Main()
    {
        Console.WriteLine("in main");
        foreach (int i in GetInts())
        {
            Console.WriteLine("in foreach");
            break; 
        }
    }
    static IEnumerable<int> GetInts()
    {
        Console.WriteLine("in GetInts");
        while (true)
        {
            Console.WriteLine("in while");
            try
            {
                Console.WriteLine("in outer try");
                try
                {
                    Console.WriteLine("in inner try before yield");
                    yield return 1;
                    Console.WriteLine("in inner try after yield");
                    break;
                }
                finally
                {
                    Console.WriteLine("in inner finally");
                }
            }
            finally
            {
                Console.WriteLine("in outer finally");
            }
        }
    }
}

5

Árboles de expresión

Ya no se produce la conversión boxing incorrecta de expresiones de método en árboles de expresión.

En la versión de lanzamiento original de Visual C# 2008, el código siguiente genera 7, 0. La línea Console.WriteLine(e.Compile()(default(T))); produce cero como resultado porque se aplica una conversión boxing incorrecta a S. En Visual C# 2008 SP1, no se produce ninguna conversión boxing y el programa produce 7, 7 como resultado.

using System;
using System.Linq;
using System.Linq.Expressions;
class Program
{
    static void Main()
    {
        Test<S>();
    }
    static void Test<T>() where T : I
    {       
        Expression<Func<T, int>> e = x => x.SetX() + x.X;
// No boxing in SP1:
        Console.WriteLine(e.Compile()(default(T))); 
    }
}
interface I
{
    int X { get; }
    int SetX();
}
struct S : I
{
    public int X { get; private set; }
    public int SetX()
    {
        X = 7;
        return 0;
    }
}

6

Inicializadores de objeto

Se ha corregido la inicialización de los tipos de valor en los inicializadores de objeto.

En la versión de lanzamiento original de Visual C# 2008, la variable local b del ejemplo siguiente no se inicializa correctamente y su miembro X tiene un valor de cero. En Visual C# 2008 SP1, S.X se inicializa correctamente en 1 en ambas expresiones new.

using System;
using System.Linq;
using System.Linq.Expressions;
    class Program
    {
        static void Main()
        {
            Test<S>();
        }
        static void Test<T>() where T : I, new()
        {
            var a = new T();
            a.X = 1;
            Console.WriteLine(a.X);
            var b = new T { X = 1 };
            Console.WriteLine(b.X);
        }
    }
    interface I
    {
        int X { get; set; }
    }
    struct S : I
    {
        public int X { get; set; }
    }
// Original release version of Visual C# 2008 output: 1 0
// Visual C# 2008 SP1 output: 1 1

7

Conversiones de tipo

Los literales null ya no se pueden convertir en valores enum.

En la versión de lanzamiento original de Visual C# 2008, en algunos casos se permite convertir los literales null en valores enum. En Visual C# 2008 SP1, se generan el Error del compilador CS1502 y el Error del compilador CS1503 si se intenta esta operación, tal como se muestra en el ejemplo siguiente.

enum MyEnum
{
    Zero = 0,
    One = 1
}
class MyClass { }
class Program
{
    static void Main(string[] args)
    {
// Produces CS1502 and CS1503:
        Test((MyClass)null);         }
    static void Test(MyEnum x)
    {
        System.Console.WriteLine(x);
    }
}

8

Árboles de expresión

El árbol de expresión no válido produce ahora la excepción correcta.

En la versión de lanzamiento original de Visual C# 2008, un árbol de expresión que contiene una llamada a un método que no está incluido en el tipo especificado produce System.Security.VerificationException. En Visual C# 2008 SP1, se produce System.ArgumentException, como se muestra en el código siguiente.

using System;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
class Program
{
    public struct S { }
    static void Main()
    {
        Type t = typeof(System.Enum);
        MethodInfo m = t.GetMethod("GetTypeCode");
        ParameterExpression p = Expression.Parameter(typeof(S), "s");
        Expression<Func<S, TypeCode>> e = Expression.Lambda<Func<S, TypeCode>>(
// Throws System.ArgumentException in Visual C# 2008 SP1:
            Expression.Call(p, m), p); 
        Func<S, TypeCode> f = e.Compile();
// Throws System.Security.VerificationException in the
// original release version of Visual C# 2008: 
        Console.WriteLine(f(new S())); 
    }
}

9

Atributos

CharSet.Unicode se propaga ahora a los tipos de aplicación auxiliar que C# genera para los campos de matriz fija.

El compilador de C# genera tipos de aplicación auxiliar para encapsular las matrices fijas. En la versión de lanzamiento original de Visual C# 2008 y versiones anteriores, el diseño de la matriz es siempre ANSI, aunque el atributo StructLayout especifique CharSet.Unicode. En el código fuente de C# no había manera de cambiar este diseño. En Visual C# 2008 SP1, se utiliza cualquier valor de CharSet que se especifique en el atributo StructLayout para construir la clase de aplicación auxiliar, tal como se muestra en el código siguiente.

using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
unsafe struct Test
{
    public fixed char Chars[8];
}
class Program
{
    static void Main(string[] args)
    {
    }
}
Original release version of Visual C# 2008 MSIL:
.class sequential ansi sealed nested public beforefieldinit '<Chars>e__FixedBuffer0'
       extends [mscorlib]System.ValueType
{
  // ... 
} // end of class '<Chars>e__FixedBuffer0'
Visual C# 2008 SP1 MSIL:
.class sequential unicode sealed nested public beforefieldinit '<Chars>e__FixedBuffer0'
       extends [mscorlib]System.ValueType
{
  // . . . 
} // end of class '<Chars>e__FixedBuffer0'

10

Comprobación de desbordamiento

stackalloc realiza ahora una comprobación de desbordamiento.

En la versión de lanzamiento original de Visual C# 2008 es posible que una asignación stackalloc produzca un error sin producir una excepción. Esto se debe a una instrucción mul no comprobada en el lenguaje intermedio de Microsoft (MSIL) generado cuando la longitud de la matriz se multiplica por el tamaño de cada elemento. En Visual C# 2008 SP1, se genera una instrucción mul.ovf en lugar de mul, de modo que los desbordamientos generan System.OverflowException cuando se intenta realizar la asignación en tiempo de ejecución.

class Program
{
    static void Main(string[] args)
    {
        int var = 0x40000000;
        unsafe
        {
            // 0x40000000 * sizeof(int) does not fit in an int.
            int* listS = stackalloc int[var]; 
// Visual C# 2008 SP1: System.OverflowException.
            listS[0] = 5; 
// Original release version of Visual C# 2008: 
// System.NullReferenceException.
        }
    }
}

11

Operadores de consulta estándar

Las consultas en colecciones no genéricas utilizan ahora semántica de conversión de tipos de C# estándar.

En las expresiones de consulta LINQ en colecciones no genéricas como System.Collections.ArrayList, el compilador vuelve a escribir la cláusula from de la consulta para incluir una llamada al operador Cast<T>. Cast<T> convierte todos los tipos de elemento al tipo especificado en la cláusula from de la consulta. Además, en la versión de lanzamiento original de Visual C# 2008, el operador Cast<T> también realiza algunas conversiones de tipo de valor y conversiones definidas por el usuario. Sin embargo, estas conversiones se realizan utilizando la clase System.Convert en lugar de la semántica de C# estándar. Estas conversiones también producen importantes problemas de rendimiento en ciertos escenarios. En Visual C# 2008 SP1, se modifica el operador Cast<T> para producir una excepción InvalidCastException para el tipo de valor numérico y las conversiones definidas por el usuario. Este cambio elimina tanto la semántica de conversión de tipo no estándar de C# y el problema de rendimiento. Este cambio se muestra en el siguiente ejemplo.

using System;
using System.Linq;
class Program
{
    public struct S { }
    static void Main()
    {
        var floats = new float[] { 2.7f, 3.1f, 4.5f };
        var ints = from int i in floats 
                   select i;
// Visual C# 2008 SP1 throws InvalidCastException. 
        foreach (var v in ints) 
            Console.Write("{0} ", v.ToString());
        // The original release version of Visual C# 2008
        // compiles and outputs 3 3 4
    }
}

Cambios problemáticos en la versión de lanzamiento original de Visual C# 2008

En la tabla siguiente se enumeran todos los cambios problemáticos de la versión de lanzamiento original de Visual C# 2008 que podrían impedir la compilación de una aplicación creada en Visual C# 2005 o que podrían cambiar su comportamiento en tiempo de ejecución.

Número de cambio

Category

Problema

Descripción

12

Conversiones de tipo

Ahora se permite la conversión a enum de cualquier expresión constante con un valor de cero.

Un literal 0 se puede convertir implícitamente a cualquier tipo de enumeración. En Visual C# 2005 y versiones anteriores del compilador, existen también algunas expresiones constantes que se evalúan como 0 que se pueden convertir implícitamente en cualquier tipo de enumeración, pero la regla que determina cuáles de estas expresiones se pueden convertir no está clara. En Visual C# 2008, todas las expresiones constantes que son iguales a 0 se pueden convertir implícitamente en cualquier tipo de enumeración.

Esto podría producir algunos cambios en el comportamiento del código existente, como la resolución de sobrecarga de los métodos que se basa en la ausencia de esta conversión implícita. El código siguiente compila correctamente en Visual C# 2005 y compiladores anteriores, ya que resuelve la llamada al método en el valor short solo con la sobrecarga de tipo entero. En Visual C# 2008, esta llamada es ambigua porque el valor short también se puede convertir implícitamente en E. En Visual C# 2008, el comportamiento cambia para permitir la conversión de cualquier expresión constante que se evalúe como cero.

public enum E
{
    Zero = 0,
    One = 1,
} 
class A
{
    public A(string s, object o)
    { System.Console.WriteLine("{0} => A(object)", s); } 
    public A(string s, E e)
    { System.Console.WriteLine("{0} => A(Enum E)", s); }
} 
class B
{
    static void Main()
    {
        A a1 = new A("0", 0);
        A a2 = new A("1", 1);
        A a3 = new A("(int) E.Zero", (int) E.Zero);
        A a4 = new A("(int) E.One", (int) E.One);
    }
}
Visual C# 2005 output:
0 => A(Enum E)
1 => A(object)
(int) E.Zero => A(object)
(int) E.One => A(object)
Visual C# 2008 output:
0 => A(Enum E)
1 => A(object)
(int) E.Zero => A(Enum E)
(int) E.One => A(object)

13

Atributos

Ahora el error se produce cuando el mismo atributo TypeForwardedTo está presente dos veces en un ensamblado.

En Visual C# 2005, no se produce ningún error si un ensamblado contiene dos atributos System.Runtime.CompilerServices.TypeForwardedTo que tienen como destino el mismo tipo. En Visual C# 2008, se genera el Error del compilador CS0739, como se muestra en el ejemplo siguiente.

// Class1.cs
// Causes CS0739:
    [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Test))]
    [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Test))] 
    public class Test
    {
        public static int Main()
        {
            Test f = new Test();
            return f.getValue();
        }
    }
    // Library1.cs
    public class Test
    {
        public int getValue()
        {
            return 0;
        }

}

14

Errores de tipo

Se ha agregado una nueva advertencia sobre el uso de un miembro de tipo de referencia en una estructura.

Las reglas de asignación definitiva de las estructuras requieren que la estructura se establezca en una instancia existente de su tipo o que cada uno de sus miembros se asigne a ella antes de crear la referencia. En Visual C# 2005, no se produce ninguna advertencia ni error cuando se utiliza un miembro de tipo de referencia sin asignar de una estructura. En Visual C# 2008, se genera el Advertencia del compilador (nivel 1) CS1060, como se muestra en el ejemplo siguiente.

    public class U { public int i;}
    public struct T { public U u;}
    class Program
    {
        static void Main()
        {
            T t;
// Produces CS1060:    
            t.u.i = 0; 
        }
    }

15

Comprobación de desbordamiento

Se ha corregido la comprobación del intervalo en los tipos decimales const.

En Visual C# 2005, al convertir los tipos decimales const, la comprobación del intervalo no siempre es correcta y se pueden provocar errores incorrectos del compilador. En Visual C# 2008, el código siguiente genera el error correcto: Error del compilador CS0031.

        static void Main()
        {
            const decimal d = -10m;
            unchecked
            {
                const byte b = (byte)d; //CS0031
            }
        }

16

Comprobación de desbordamiento

Las conversiones fuera de los límites a long generan ahora el error correcto del compilador.

En Visual C# 2005, el código siguiente no genera ningún error del compilador. En Visual C# 2008, genera el Error del compilador CS0031.

class Conversion 
    {
        static void Main() 
        {
            long l2 = (long) 9223372036854775808M; //CS0031 
        }
    }

17

Búferes de tamaño fijo

El acceso a un búfer de tamaño fijo en una estructura no segura antes de asignar un valor al búfer genera ahora un error del compilador.

Las reglas de asignación definitiva para los punteros no seguros requieren que se establezca el puntero antes de eliminar las referencias del puntero. En Visual C# 2005, cuando una estructura no segura contiene un puntero a una matriz, el acceso al puntero antes de asignarle un valor, no generaba ningún error del compilador. En Visual C# 2008, genera el Error del compilador CS0165, como se muestra en el código siguiente.

    unsafe class Test
    {
        static void Main()
        {
            S* ps;
            ps->i[0]++;        } // CS0165
    }
    unsafe struct S
    {
        public fixed int i[10];
    }

18

Los efectos secundarios se conservan ahora en las expresiones de uso combinado de NULL.

Asignación definitiva y el operador ??.

En Visual C# 2005, en ciertos escenarios, no se conservan los efectos secundarios en el lado izquierdo de una expresión de uso combinado de NULL. En estos casos, la segunda instrucción Console.WriteLine del ejemplo siguiente genera un error incorrecto del compilador que indica que no se ha asignado b. En Visual C# 2008, el mismo código compila correctamente sin ningún error.

        static void Main()
        {
            int? a, b;
            a = null;
            Console.WriteLine((b = null) ?? 17);
// No error in Visual C# 2008:Console.WriteLine(a + b);  

}

19

try-finally en iteradores

Ahora el bloque finally se ejecuta cuando un iterador del bloque try escapa con continue o goto.

En Visual C# 2005, en una construcción try-finally, cuando un control sale de un bloque de iteradores en el bloque try mediante una instrucción goto o continue, el bloque finally no se ejecuta. En Visual C# 2008, en estos casos se ejecuta el bloque finally.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DisposeTest
{
    class A : IDisposable
    {
        int m_n;
        internal A(int n)
        {
            m_n = n;
        }
        internal void Nothing() { }
        ~A()
        {
            Console.WriteLine("failed to dispose {0}", m_n);
        }
        #region IDisposable Members
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Console.WriteLine("dispose {0}", m_n);
        }
        #endregion
    }
    class Program
    {
        static IEnumerable<A> B()
        {
            for (int nCount = 0; nCount < 2; nCount++)
            {
                Console.WriteLine("loop start");
                using (A A = new A(nCount))
                {
                    Console.WriteLine("using start");
                    // Section 1.
                    // Dispose not called correctly in Visual C# 2005.
                    if ((nCount % 2) == 0)
                        continue;
                    // Section 2.
                    // Dispose not called correctly in Visual C# 2005.
                    yield return A;
                    Console.WriteLine("using end");
                }
                Console.WriteLine("loop end");
            }
            yield break;
        }
        static void Main(string[] args)
        {
            foreach (A A in B())
            {
                A.Nothing();
            }
            Console.ReadLine();
        }
    }

}

20

Clases base e interfaces

La construcción de clases omite ahora las implementaciones explícitas de los mismos miembros de interfaz en las clases base.

En Visual C# 2005, cuando una clase no proporciona una implementación para un miembro de interfaz, el compilador sustituye las implementaciones de la clase base aunque se declaren como implementaciones explícitas de interfaz. Este comportamiento no cumple con la especificación de la Asociación europea de fabricantes de informática (ECMA). Visual C# 2008 implementa correctamente la especificación. En el ejemplo siguiente, Visual C# 2005 imprime "B.Test". Visual C# 2008 imprime correctamente "A.Test" y omite el método Test de la clase B porque es una implementación de interfaz explícita.

using System;
interface ITest
{
    string Test { get; }
    string Test2 { get; }
}
class A : ITest
{
    public string Test { get { return "A.Test"; } }
    public string Test2 { get { return "A.Test2"; } }
}
class B : A, ITest
{
    string ITest.Test { get { return "B.Test"; } }
    string ITest.Test2 { get { return "B.Test2"; } }
}
class C : B, ITest
{
    string ITest.Test2 { get { return "C.Test2"; } }
}
class Program
{
    static void Main()
    {
        C c = new C();
        Console.WriteLine(c.Test); 
// Visual C# 2008: "A.Test"
    }

}

21

Atributos

El uso de un miembro obsoleto genera ahora una advertencia del compilador.

Puede marcar los métodos con el atributo Obsolete para producir errores o advertencias en tiempo de compilación si se invocan los métodos. Al incluir este atributo en métodos virtuales, se debe colocar en el método base. Si el atributo Obsolete se incluye en un método de invalidación, no producirá errores ni advertencias del compilador al invocar el método. En Visual C# 2005, el compilador permitía incluir el atributo Obsolete en un método de invalidación aunque no tenía ningún efecto en este caso. En Visual C# 2008, se produce la advertencia del compilador Advertencia del compilador (nivel 1) CS0809, "El miembro obsoleto 'A.Filename' invalida el miembro no obsoleto 'Error.Filename'". En el ejemplo siguiente se genera esta advertencia:

class A : Error
{
    [System.ObsoleteAttribute("Obsolete", true)]
    public override string Filename
    {
        set
        {
        }
    }
    public static void Main() { }
}
public class Error
{
    public virtual string Filename
    {
        set
        {
        }
        get
        {
            return "aa";
        }
    }
}
class B
{
    void TT()
    {
        new A().Filename = "Filename";
    }
}

22

Errores de compilación

El uso de la opción del compilador /pdb sin /debug genera ahora un error.

En Visual C# 2005, no se muestra ninguna advertencia ni error al especificar la opción /pdb pero no la opción /debug. Visual C# solo crea una versión de lanzamiento sin generar el archivo .pdb. En la versión de lanzamiento original de Visual C# 2008, si especifica /pdb sin especificar también /debug, el compilador muestra el Error del compilador CS2036.

23

Errores de tipo

Ahora se genera un error cuando una condición switch es void.

En Visual C# 2005, no se genera ningún error cuando se utiliza una invocación de método void en una instrucción switch. En Visual C# 2008, se genera el Error del compilador CS0151.

class C
{
    static void Main()
    {
// Produces CS0151:
        switch (M()) 
        {
            default:
                break;
        }
    }
    static void M()
    {
    }

}

24

Comprobación de desbordamiento

Ahora las conversiones de decimal constante a entero generan un error diferente del compilador.

En Visual C# 2005, el código siguiente genera el Error del compilador CS0133: "La expresión que se asigne a 'b' debe ser constante".

const byte b = unchecked((byte)256M);

En Visual C# 2008, se genera el Error del compilador CS0031: "El valor constante '256M' no se puede convertir en 'byte'". Observe que el error se genera aunque se aplique el modificador unchecked.

25

Expresiones constantes

La especificación se cumple mejor en lo que respecta a las expresiones constantes.

En Visual C# 2008, se han corregido varios problemas por los que Visual C# 2005 permitía incorrectamente operadores y variables en expresiones constantes. En Visual C# 2005, el código siguiente compila sin errores. En Visual C# 2008, se generan el Error del compilador CS0165, la Advertencia del compilador (nivel 1) CS0184 y la Advertencia del compilador (nivel 3) CS1718:

class Program
{
    public static int Main()
    {
        int i1, i2, i3, i4, i5;
        // 'as' is not permitted in a constant expression.
        if (null as object == null)
            i1 = 1;
        // 'is' is not permitted in a constant expression.
        if (!(null is object))
            i2 = 1;
        // A variable is not permitted in a constant expression.
        int j3 = 0;
        if ((0 == j3 * 0) && (0 == 0 * j3))
            i3 = 1;
        int j4 = 0;
        if ((0 == (j4 & 0)) && (0 == (0 & j4)))
            i4 = 1;
        int? j5 = 1;
// Warning CS1718: Comparison made to same variable:
        if (j5 == j5) 
 
            i5 = 1;
        System.Console.WriteLine("{0}{1}{2}{3}{4}{5}", i1, i2, i3, i4, i5);
        return 1;
    }
}

26

Errores de tipo

Ahora se genera un error cuando se utiliza un tipo estático como parámetro en un delegado o en una expresión lambda.

En Visual C# 2005, no se genera ningún error si se utiliza un tipo estático como parámetro de un delegado o un método anónimo. Los tipos estáticos no se pueden utilizar como tipos de parámetros de método porque no se pueden crear instancias de ellos. La versión de Visual C# 2005 del compilador permite los tipos estáticos como tipos de parámetro dentro de delegados y declaraciones de método anónimo. Si pasa null como parámetro, se pueden invocar estos delegados. En Visual C# 2008, se genera el Error del compilador CS0721 si se utiliza un tipo estático como parámetro de un delegado o un método anónimo, como se muestra en el ejemplo siguiente.

public static class Test { }
public class Gen<T> { }
// Produces CS0721:
delegate int D(Test f); 
public class TestB
{
    public static void Main()
    {
        D d = delegate(Test f) { return 1; };
    }

}

27

Tipos que aceptan valores NULL y expresiones ??

Cuando se convierte una constante en un tipo que acepta valores NULL antes de asignarlo a un tipo que acepta valores NULL (más amplio), no se produce ninguna advertencia.

En Visual C# 2005, el código siguiente genera la Advertencia del compilador (nivel 3) CS0219. En Visual C# 2008, no se genera ninguna advertencia.

ushort? usq2 = (byte?)0;

28

Resolución de sobrecarga

Ahora se genera un error cuando se produce una resolución de sobrecarga ambigua en métodos anónimos.

El compilador debe resolver las invocaciones de método en métodos sobrecargados para determinar qué sobrecarga concreta se debe invocar. Cuando se infiere parcialmente el tipo de parámetro de una invocación, puede que la sobrecarga concreta que se debe invocar resulte ambigua. Esto provoca un error del compilador.

Cuando se pasa un método anónimo como parámetro de un delegado, se infiere parcialmente el tipo de delegado del método anónimo. Esto puede provocar ambigüedad cuando el compilador selecciona la sobrecarga correcta.

En Visual C# 2005, el compilador no siempre genera un error cuando no existe una única sobrecarga preferible para un método anónimo. En Visual C# 2008, se genera el Error del compilador CS0121, como se muestra en el ejemplo siguiente.

class Program
{
    static int ol_invoked = 0;
    delegate int D1(int x);
    delegate T D1<T>(T x);
    delegate T D1<T, U>(U u);
    static void F(D1 d1) { ol_invoked = 1; }
    static void F<T>(D1<T> d1t) { ol_invoked = 2; }
    static void F<T, U>(D1<T, U> d1t) { ol_invoked = 3; }
    static int Test001()
    {
// Produces CS0121:
        F(delegate(int x) { return 1; });         if (ol_invoked == 1)
            return 0;
        else
            return 1;
    }
    static int Main()
    {
        return Test001();
    }
}

29

Errores de tipo

Ahora se genera un error si declara una matriz de punteros a tipos administrados.

No se permiten los punteros no seguros a tipos de referencia; generan errores del compilador. En Visual C# 2005, es posible declarar una matriz de punteros a tipos administrados. En Visual C# 2008, se genera el Error del compilador CS0208: "No se puede adquirir la dirección, obtener el tamaño ni declarar un puntero a un tipo administrado ('T')".

unsafe class TestClass<T>
{
// Produces CS0208:
    static T*[] x = { }; 
// Produces CS0208:
    static void Test(T*[] arr) 
    {
    }
// Produces CS0208:
    static T*[] TestB() 
    {
        return x;
    }

}

30

Resolución de sobrecarga

Ahora se genera una advertencia cuando los métodos del candidato de resolución de sobrecarga varían solo en ref o out.

En Visual C# 2005, cuando el compilador de C# realiza la resolución de sobrecarga en tipos genéricos, no comprueba si los argumentos de tipo harán que los métodos de candidato varíen solo en ref o out. Como resultado, la selección del método queda a cargo de Common Language Runtime (CLR) en tiempo de ejecución, que simplemente selecciona el primer método de la lista. En Visual C# 2008, se genera la Advertencia del compilador (nivel 1) CS1956 cuando el compilador detecta que dos métodos de candidato para la resolución de sobrecarga variarán solo en ref o out. Esta condición se muestra en el siguiente ejemplo.

using System;
class Base<T, S>
{
// Produces CS1956:
    public virtual void Test(out T x) 
    {
        Console.WriteLine("Test(out T x)");
        x = default(T);
    }
    public virtual void Test(ref S x)
    {
        Console.WriteLine("Test(ref T x)");
    }
}
interface IFace
{
    void Test(out int x);
}
class Derived : Base<int, int>, IFace
{
    static void Main()
    {
        IFace x = new Derived();
        int y;
        x.Test(out y);
    }

}

31

Tipos que aceptan valores NULL y expresiones ??

Una expresión de uso combinado de NULL con NULL en el lado izquierdo ya no se evalúa como una constante NULL.

En Visual C# 2005, una expresión de uso combinado de NULL con NULL en el lado izquierdo se evalúa como una constante NULL. En Visual C# 2008, esto ya no sucede así. En algunos casos, el comportamiento de Visual C# 2005 permite que las variables se traten de manera incorrecta como variables asignadas definitivamente. El código siguiente compila y se ejecuta sin error en Visual C# 2005, pero en Visual C# 2008, se genera el Error del compilador CS0165: "Uso de la variable local no asignada 'x'".

static void Main()
    {
        int x;
        if (null == (decimal?)(null ?? null)) x = 1;
        // Producers CS0165 in Visual C# 2008:
        System.Console.WriteLine(x);    
    }

Vea también

Otros recursos

Introducción a Visual C#

Historial de cambios

Fecha

Historial

Motivo

Julio de 2008

Se ha agregado un tema.

Cambio de características de SP1.