Visual C# 2008 の互換性に影響する変更点

更新 : 2008 年 7 月

Visual C# 2008 Service Pack 1 の互換性に影響する変更点

次の表に、Visual C# 2008 の元のリリース バージョンまたは Visual C# 2005 で作成したアプリケーションに影響を与える可能性がある、Visual C# 2008 Service Pack 1 の互換性に影響する変更点の一覧を示します。

変更番号

カテゴリ

問題

説明

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 パラメータがあり、インターフェイス定義にはパラメータがないインデクサの明示的な実装を定義できます。この構造は仕様に反しています。Visual C# 2008 SP1 では、次のコードに示すように、この構造は コンパイラ エラー 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" が出力されます。Visual C# 2008 Service Pack 1 では、コンパイラの警告 (レベル 3) CS1718 が生成され、"true" が出力されます。

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

4

反復子における try-finally

break ステートメントがある、入れ子になった finally ブロックの実行が変更されています。

Visual C# 2008 の元のリリース バージョンでは、以下のコードで外側の finally が 2 回実行されます。Visual C# 2008 SP1 では、外側の finally は 1 回実行されます。

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 を出力します。S が誤ってボックス化されるため、行 Console.WriteLine(e.Compile()(default(T))); は 0 を出力します。Visual C# 2008 SP1 では、ボックス化は行われず、プログラムは 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 の値は 0 になります。Visual C# 2008 SP1 では、どちらの new 式でも S.X は正しく 1 に初期化されます。

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 リテラルは場合によって列挙値に変換できます。Visual C# 2008 SP1 では、これを実行しようとすると、次の例に示すように コンパイラ エラー 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 がスローされます。Visual C# 2008 SP1 では、次のコードに示すように、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 の元のリリース バージョンとそれ以前のバージョンでは、StructLayout 属性で CharSet.Unicode が指定されていても、配列のレイアウトは常に ANSI になります。C# のソース コードでそれを変更する方法はありませんでした。Visual C# 2008 SP1 では、次のコードに示すように、StructLayout 属性で指定された CharSet 値を使用してヘルパー クラスが構築されます。

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 の割り当てが、例外を発生せずに失敗することがありました。これは、配列の長さに各要素のサイズを掛けるときに、生成された Microsoft Intermediate Language (MSIL) 内の mul 命令をチェックしていないためです。Visual C# 2008 SP1 では、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# キャスト セマンティクスが使用されるようになりました。

System.Collections.ArrayList などの、非ジェネリック コレクションに対する LINQ クエリ式において、クエリの from 句は、コンパイラによって Cast<T> 演算子の呼び出しを含むように書き換えられます。Cast<T> は、すべての要素型を、クエリの from 句で指定された型に変換します。また、Visual C# 2008 の元のリリース バージョンでは、Cast<T> 演算子はいくつかの値型変換とユーザー定義の変換も実行します。しかし、これらの変換は、標準の C# セマンティクスではなく、System.Convert クラスを使用して実行されます。これらの変換により、特定のシナリオで重大なパフォーマンス上の問題が発生します。Visual C# 2008 SP1 では、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# 2005 で作成したアプリケーションがコンパイルできなくなったり、実行時の動作が変わったりする可能性がある、Visual C# 2008 の元のリリース バージョンの互換性に影響する変更点の一覧を示します。

変更番号

カテゴリ

問題

説明

12

型変換

値が 0 の定数式から列挙型への変換ができるようになりました。

リテラル 0 は、任意の列挙型に暗黙的に変換できます。Visual C# 2005 およびそれ以前のバージョンのコンパイラでは、任意の列挙型に暗黙的に変換できる、評価結果が 0 になる定数式もありましたが、これらの式のうちどちらが変換可能であるかを決定する規則が不明確でした。Visual C# 2008 では、0 に等しいすべての定数式が任意の列挙型に暗黙的に変換できます。

これにより、この暗黙的な変換が存在しないことを利用しているメソッドのオーバーロードの解決など、既存のコードの動作が若干変わる可能性があります。次のコードは Visual C# 2005 およびそれ以前のバージョンのコンパイラでは正常にコンパイルされますが、short 値に対するメソッドの呼び出しは、int のオーバーロードにのみ解決されます。Visual C# 2008 では、short 値も 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

属性

1 つのアセンブリに同じ TypeForwardedTo 属性が 2 つあるとエラーが発生するようになりました。

Visual C# 2005 では、アセンブリに同じ型を対象とする 2 つの 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 decimal 型に対する範囲チェックが修正されました。

Visual C# 2005 では、const decimal 型をキャストすると、範囲チェックが正しく実行さないことがあり、誤ったコンパイラ エラーが発生することがあります。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

null 合体式における副作用の維持

確実な代入と ?? 演算子。

Visual C# 2005 では、特定のシナリオにおいて、null 合体式の左側に対する副作用は維持されません。その場合、次の例の第 2 の 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

try ブロック内の反復子が continue または goto でエスケープすると、finally ブロックが実行されるようになりました。

Visual C# 2005 では、try-finally 構造で、goto ステートメントまたは continue ステートメントを使用して、制御が try ブロック内の反復子ブロックの外に渡ると、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 では、クラスにインターフェイス メンバの実装がないと、コンパイラは、基本クラスの実装が明示的なインターフェイスの実装として宣言されている場合でも、その実装を置き換えます。この動作は、ECMA (European Computer Manufacturers Association) 仕様に準拠していません。Visual C# 2008 では、仕様が正しく実装されています。次の例で、Visual C# 2005 では "B.Test" が表示されます。Visual C# 2008 では正しく "A.Test" が表示され、クラス B の Test メソッドが無視されます。これは、このメソッドが明示的なインターフェイスの実装であるためです。

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 が生成され、"旧形式のメンバ 'ファイル名' は、旧形式ではないメンバ 'エラー ファイル名' をオーバーライドします" と表示されます。この警告は次のような例で発生します。

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 の元のリリース バージョンでは、/debug を指定せずに /pdb を指定すると、コンパイラによって コンパイラ エラー 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

オーバーフロー チェック

10 進定数から整数への変換で、異なるコンパイル エラーが生成されるようになりました。

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 では、匿名メソッドに対する最適なオーバーロードが 1 つに定まらない場合でも、コンパイラは常にエラーを生成するわけではありません。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 では、オーバーロードの解決の候補となる 2 つのメソッドで 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 の null 合体式は、null 定数として評価されなくなりました。

Visual C# 2005 では、左側が null の null 合体式は、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 年 7 月

トピックを追加

SP1 機能変更