Критические изменения в Visual C# 2008

Обновлен: Июль 2008

Критические изменения в пакете обновления 1 (SP1) для Visual C# 2008

В следующей таблице перечислены все критические изменения в пакете обновления 1 (SP1) для Visual C# 2008, которые могут повлиять на приложение, созданное в исходной версии выпуска Visual C# 2008 или в Visual C# 2005.

Номер изменения

Категория

Проблема

Описание

1

Разрешение перегрузки

Теперь в массивы типов указателей в разрешении перегрузки методов входит определение типа.

В Visual C# 2008 и более ранних версиях определение типа исключает массивы типов указателей из процесса разрешения перегрузки метода. В следующем коде компилятор Visual C# 2005 выбирает версию Test, не являющуюся общей, поскольку общая версия Test не рассматривается из-за ее параметра типа int*[]. В Visual C# 2008 выбирается общая версия 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

Индексаторы

Теперь компилятор вызывает ошибку CS0466 не только для методов, но и для индексаторов и свойств.

В исходной версии Visual C# 2008 и более ранних версиях можно определять явную реализацию индексатора, где реализация имеет параметр params, который, в свою очередь, отсутствует в определении интерфейса. Такая конструкция противоречит спецификации. В пакете обновления 1 (SP1) для Visual C# 2008 эта конструкция вызывает Ошибка компилятора CS0466, как показано в следующем коде.

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

}

3

Типы, допускающие значения NULL, и выражения ??

Теперь компилятор правильно вычисляет выражения, в которых переменные, допускающие значения NULL, сравниваются друг с другом.

В исходной версии Visual C# 2008 следующий код выполняет компиляцию и выдает результат со значением "false" во время выполнения. В пакете обновления 1 (SP1) для Visual C# 2008 выводится Предупреждение компилятора (уровень 3) CS1718, а результатом является значение "true".

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

4

try-finally в итераторах

Внесены изменения в процесс выполнения вложенных блоков finally итераторов, имеющих операторы break.

В исходной версии Visual C# 2008 следующий код выполняет внешний блок finally два раза. В пакете обновления 1 (SP1) для Visual C# 2008 внешний блок finally выполняется один раз.

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

Деревья выражений

Исключена неверная упаковка-преобразование выражений методов в дереве выражений.

В исходной версии Visual C# 2008 результатом выполнения следующего кода является 7, 0. Строка Console.WriteLine(e.Compile()(default(T))); выдает 0, поскольку S упаковывается неправильно. В пакете обновления 1 (SP1) для Visual C# 2008 упаковка-преобразование не выполняется, и выходными данными программы является 7, 7.

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

Инициализаторы объектов

Внесены исправления в процесс инициализации типов значений в инициализаторах объектов.

В исходной версии Visual C# 2008 локальная переменная b, представленная в следующем примере, инициализирована неверно, и значение ее члена X равно нулю. В пакете обновления 1 (SP1) для Visual C# 2008 S.X правильно инициализирован со значением 1 в обоих выражениях 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

Преобразования типов

Литералы NULL больше не преобразуются в перечисляемые значения.

В исходной версии Visual C# 2008 в некоторых случаях допускается преобразование литералов NULL в перечисляемые значения. В пакете обновления 1 (SP1) для Visual C# 2008 при попытке выполнить преобразование возникают Ошибка компилятора CS1502 и Ошибка компилятора CS1503, как показано в следующем примере.

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

Деревья выражений

Теперь недопустимое дерево выражений создает правильное исключение.

В исходной версии Visual C# 2008 дерево выражений, содержащее вызов метода, который не принадлежит к указанному типу, вызывает исключение System.Security.VerificationException. В пакет обновления 1 (SP1) для Visual C# 2008 создается исключение System.ArgumentException, как показано в следующем коде.

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

Атрибуты

Теперь CharSet.Unicode распространяется на все вспомогательные типы, созданные C# для фиксированных полей массивов.

Компилятор C# создает вспомогательные типы для инкапсуляции фиксированных массивов. В исходной версии Visual C# 2008 и более ранних версиях массив всегда имеет структуру ANSI, даже если атрибут StructLayout задает CharSet.Unicode. Изменить данный момент в исходном коде C# было невозможно. В пакете обновлений 1 (SP1) для Visual C# 2008 значение CharSet, заданное в атрибуте StructLayout, не влияет на создание вспомогательного класса, как показано в следующем коде.

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

Проверка переполнения

Теперь stackalloc выполняет проверку переполнения.

В исходной версии Visual C# 2008 существует возможность сбоя выделения stackalloc без вызова исключения. Это связано с непроверенной инструкцией mul в созданном MSIL, когда длина массива умножается на значение размера каждого элемента. В пакете обновлений 1 (SP1) для Visual C# 2008 вместо инструкции mul создается инструкция mul.ovf, таким образом, переполнение вызывает System.OverflowEx ception, если предпринимается попытка выделения во время выполнения.

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

Стандартные операторы запросов

Теперь в запросах для коллекций, не являющихся общими, используются стандартные семантики приведения C#.

В выражениях запросов LINQ к коллекциям, не являющимся общими, например System.Collections.ArrayList, предложение from запроса переписывается компилятором для включения вызова оператора Cast<T>. Cast<T> преобразует все типы элементов в тип, указанный в предложении from в запросе. Кроме того, в исходной версии Visual C# 2008 оператор Cast<T> выполняет некоторые преобразования типов значений и пользовательские преобразования. Однако вместо стандартных семантик C# здесь используется класс System.Convert. В ряде случаев эти преобразования являются причиной возникновения значительных проблем, связанных с производительностью. В пакете обновлений 1 (SP1) для Visual C# 2008 оператор Cast<T> изменен с целью создания исключения InvalidCastException для числовых типов значений и пользовательских преобразований. Это изменение исключает необходимость использования нестандартных семантик приведения C# и возникновение проблем с производительностью. Оно показано в следующем примере.

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
    }
}

Критические изменения в исходной версии Visual C# 2008

В следующей таблице перечислены все важные изменения в исходной версии Visual C# 2008, которые могут заблокировать компиляцию приложения, созданного в Visual C# 2005, или могут изменить его поведение во время выполнения.

Номер изменения

Категория

Проблема

Описание

12

Преобразования типов

Теперь можно выполнять преобразование любых константных выражений со значением, равным нулю, в перечисления.

Литерал 0 неявным образом преобразуется в любой тип перечисления. В Visual C# 2005 и более ранних версиях компилятора существует несколько константных выражений со значением 0, которые можно неявно преобразовать в любой тип перечисления, однако правило, определяющее, какие из этих выражений являются преобразуемыми, непонятно. В Visual C# 2008 все константные выражения со значением 0 могут быть неявным образом преобразованы в любой тип перечисления.

В результате этого могут появиться изменения в действии существующего кода, например разрешение перегрузки, которое основывается на отсутствии подобного неявного преобразования. Следующий код успешно компилируется в компиляторах Visual C# 2005 и более ранних версий, разрешая вызов метода в коротком значении только для int-перегрузки. В Visual C# 2008 это вызов является неоднозначным, поскольку короткое значение также можно преобразовать в E. В Visual C# 2008 поведение изменилось, чтобы разрешить преобразования константных выражений со значением 0.

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

Атрибуты

Если один и тот же атрибут TypeForwardedTo дважды присутствует в одной сборке, происходит ошибка.

Если сборка в Visual C# 2005 содержит два атрибута System.Runtime.CompilerServices.TypeForwardedTo одного типа, ошибка не возникает. В Visual C# 2008 возникает Ошибка компилятора CS0739, как показано в следующем примере.

// 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

Ошибки типов

Добавлено новое предупреждение об использовании в структуре члена ссылочного типа.

Правила определенного присваивания для структур требуют, чтобы либо структуре был задан существующий экземпляр ее типа, либо каждому из ее членов было присвоено значение до создания на него ссылки. В Visual C# 2005 при использовании члена ссылочного типа структуры, которому не присвоено значение, не возникает ни предупреждение, ни ошибка. В Visual C# 2008 возникает Предупреждение компилятора (уровень 1) CS1060, как показано в следующем примере.

    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

Проверка переполнения

Внесены исправления в проверку диапазона в десятичных типах const.

В Visual C# 2005 при приведении константных десятичных типов проверка диапазона не всегда выполняется корректно, что может привести к неверным ошибкам компилятора. В Visual C# 2008 следующий код вызывает правильную ошибку: Ошибка компилятора CS0031.

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

16

Проверка переполнения

Теперь выполняющиеся вне диапазона преобразования в long вызывают правильную ошибку компилятора.

В Visual C# 2005 следующий код не вызывает ошибку компилятора. В Visual C# 2008 он вызывает Ошибка компилятора CS0031.

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

17

Буферы фиксированного размера

Теперь при обращении к буферу фиксированного размера в небезопасной структуре до присвоения ему значения приводит к ошибке компилятора.

Правила определенного присваивания для небезопасных указателей требуют задать значение указателю до его разыменования. Если в Visual C# 2005 небезопасная структура содержит указатель на массив, обращение к указателю до присвоения ему значения не приведет к ошибке компилятора. В Visual C# 2008 возникает Ошибка компилятора CS0165, как показано в следующем коде.

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

18

Побочные эффекты теперь сохраняются в нулевых объединяющих выражениях

Определенное присваивание и оператор ??.

В Visual C# 2005 в ряде сценариев побочные эффекты, возникающие с левой стороны нулевого объединяющего выражения, не сохраняются. В таких случаях второй оператор Console.WriteLine в следующем примере вызывает неверную ошибку компилятора, сообщающую, что b не присвоено значение. В Visual C# 2008 тот же код компилируется правильно и без ошибок.

        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 в итераторах

Блок finally выполняется, когда итератор в блоке try переключается с continue или goto.

В Visual C# 2005 в конструкции try-finally когда элемент управления выходит из блока итератора в блок try с помощью оператора goto или continue, блок finally не выполняется. В Visual C# 2008 в таких случаях блок 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

Базовые классы и интерфейсы

Теперь конструкция класса игнорирует явную реализацию одних и тех же членов интерфейса в базовом классе.

Когда в Visual C# 2005 класс не предоставляет реализацию для члена интерфейса, компилятор заменяет реализации базовых классов, даже если они объявлены как явные реализации интерфейсов. Это поведение не соответствует спецификации ассоциации европейских производителей компьютеров (European Computer Manufacturers Association, ECMA). В Visual C# 2008 спецификация реализуется нужным образом. В следующем примере в Visual C# 2005 выводится "B.Test". В Visual C# 2008 правильно выводится "A.Test" и игнорируется метод Test в классе B, поскольку он является явной реализацией интерфейса.

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

Атрибуты

Теперь при использовании устаревшего члена создается предупреждение компилятора.

Чтобы привести к возникновению ошибок или предупреждений во время выполнения при вызове методов, методы можно пометить атрибутом Obsolete. Помещая этот атрибут в виртуальные методы, он должен быть размещен в базовом методе. Если атрибут Obsolete размещается в методе переопределения, он не приведет к ошибкам или предупреждениям компилятора при вызове. В Visual C# 2005 компилятор позволял размещать атрибут Obsolete в методе переопределения, даже если атрибут не вызывал там никаких действий. В Visual C# 2008 выводится предупреждение компилятора Предупреждение компилятора (уровень 1) CS0809: "Устаревший член "A.имя_файла"переопределяет неустаревший член "Error.имя_файла"". Следующий код вызывает это предупреждение.

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

Ошибки построения

Теперь использование параметра компилятора /pdb без /debug вызывает ошибку.

Если в Visual C# 2005 задается значение параметра /pdb, а не /debug, предупреждение или ошибка не выводятся. В Visual C# просто создается построение выпуска; файл с расширением PDB не генерируется. Если в исходной версии Visual C# 2008 задается параметр /pdb, но не задается параметр /debug, компилятор выводит ошибку Ошибка компилятора CS2036.

23

Ошибки типов

Если условие switch имеет значение void, возникает ошибка.

Если в Visual C# 2005 в операторе switch используется вызов метода void, ошибки не создаются. В Visual C# 2008 возникает Ошибка компилятора CS0151.

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

}

24

Проверка переполнения

Теперь преобразования десятичных констант в целочисленные вызывают разные ошибки компилятора.

В Visual C# 2005 следующий код вызывает ошибку Ошибка компилятора CS0133: "Назначаемое выражение для "b" должно быть константным".

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

В Visual C# 2008 возникает ошибка Ошибка компилятора CS0031: "Постоянное значение "256M" не может быть преобразовано в "byte"". Следует обратить внимание, что эта ошибка возникает даже при применении модификатора unchecked.

25

Константные выражения

Спецификация более точно соответствует константным выражениям.

В Visual C# 2008 было устранено несколько проблем, где в Visual C# 2005 неправильно использовались операторы и переменные в константных выражениях. В Visual C# 2005 следующий код компилируется без ошибок. В Visual C# 2008 возникают Ошибка компилятора CS0165, Предупреждение компилятора (уровень 1) CS0184, и Предупреждение компилятора (уровень 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

Ошибки типов

Теперь при использовании статического типа в качестве параметра в делегате или лямбда-выражении возникает ошибка.

Если в Visual C# 2005 статический тип используется в качестве параметра для делегата или анонимного метода, ошибки не возникают. Статические типы нельзя использовать в качестве типов параметров методов, поскольку они не могут быть созданы. В версии Visual C# 2005 компилятора допускается использование статических типов в качестве типов параметров в объявлениях делегатов и анонимных методов. При передаче NULL в качестве параметра могут быть вызваны такие делегаты. Если в Visual C# 2008 статический тип используется в качестве параметра для делегата или анонимного метода, возникает ошибка Ошибка компилятора CS0721, как показано в следующем примере.

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

Типы, допускающие значения NULL, и выражения ??

При приведении константы к типу, допускающему значения NULL, перед присвоением ей значения NULL (более широкого типа) предупреждение не создается.

В Visual C# 2005 следующий код вызывает Предупреждение компилятора (уровень 3) CS0219. В Visual C# 2008 предупреждение не выводится.

ushort? usq2 = (byte?)0;

28

Разрешение перегрузки

При возникновении неоднозначного разрешения перегрузки в анонимном методе выводится ошибка.

Вызовы методов в перегруженных методах должны быть разрешены компилятором, чтобы определить, какую конкретную перегрузку следует вызывать. Если тип параметра вызова определен частично, конкретная вызываемая перегрузка может стать неоднозначной. Это приводит к ошибке компилятора.

При передаче анонимного метода в качестве параметра делегата тип делегата этого метода определяется лишь частично. Это может привести к неоднозначности в процессе выбора компилятором нужной перегрузки.

В Visual C# 2005 при отсутствии одной наилучшей перегрузки для анонимного метода компилятор не всегда выводит ошибку. В Visual C# 2008 возникает Ошибка компилятора CS0121, как показано в следующем примере.

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

Ошибки типов

Теперь при объявлении массива указателей на управляемые типы возникает ошибка.

Небезопасные указатели на ссылочные типы не допускаются, поскольку приводят к ошибками компилятора. В Visual C# 2005 можно объявить массив указателей на управляемые типы. В Visual C# 2008 возникает ошибка Ошибка компилятора CS0208: "Невозможно получить адрес, размер или объявить указатель на управляемый тип ('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

Разрешение перегрузки

Теперь предупреждение выводится, когда методы-кандидаты разрешения перегрузки отличаются только по ref или out.

В Visual C# 2005, когда компилятор C# выполняет разрешение перегрузки в универсальных типах, он не проверяет, будут ли методы-кандидаты отличаться только по ref или out из-за аргументов типов. В результате выбор методов производится средой CLR во время выполнения — выбирается первый метод в списке. Когда в Visual C# 2008 компилятор обнаруживает, что два метода-кандидата для разрешения перегрузки будут отличаться только по ref или out, выводится Предупреждение компилятора (уровень 1) CS1956. Это условие показано в следующем примере.

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

Типы, допускающие значения NULL, и выражения ??

Объединяющее нулевое выражение с нулем слева больше не обрабатывается как константа null.

В Visual C# 2005 объединяющее нулевое выражение с нулем слева больше обрабатывается как константа null. В Visual C# 2008 эта ситуация изменилась. Иногда поведение Visual C# 2005 допускает неверную интерпретацию переменных как присвоенных определенным образом. В Visual C# 2005 следующий код компилируется и выполняется без ошибок, но в Visual C# 2008 возникает ошибка Ошибка компилятора CS0165: "Использование локальной переменной "x", которой не присвоено значение".

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

См. также

Другие ресурсы

Приступая к работе с Visual C#

Журнал изменений

Дата

Журнал

Причина

Июль 2008

Добавленный раздел.

Изменение функции SP1.