此浏览器不再受支持。
请升级到 Microsoft Edge 以使用最新的功能、安全更新和技术支持。
下表列出了 Visual C# 2010 中的重大更改,这些更改可能会使以前使用 Visual C# 2008 创建的应用程序无法编译,或者可能会更改这类应用程序的运行时行为。
类别
问题
说明
程序集绑定
程序集绑定将两个程序集视为等效的。
如果 C# 2010 应用程序同时引用特定引用程序集的 .NET Framework 版本和 .NET Framework for Silverlight 版本并且还使用 extern alias,则会导致编译器错误。 默认情况下,程序集绑定将两个程序集视为等效的。
若要解决此错误,可使用 /appconfig 编译器选项指定 app.config 文件的位置,并在该文件中使用 <supportPortability> 标记禁用默认行为。 有关更多信息,请参见 /appconfig(C# 编译器选项)。
如果您正在使用 Microsoft Build Engine (MSBuild) 生成应用程序,则应将适当的标记添加到 .csproj 文件。
协变和逆变
为泛型接口和委托(如 IEnumerable<T> 和 Func<TResult>)添加了新的隐式转换。
泛型接口和委托(如 IEnumerable<T> 和 Func<TResult>)现在具有了用于泛型类型参数的隐式转换。 例如,在 C# 2010 中,可将 IEnumerable<string> 隐式转换为 IEnumerable<object>,这可能导致在以下情况下产生不同行为。
使用 is 关键字检查类型兼容性。
IEnumerable<string> strings = new List<string>(); if (strings is IEnumerable<object>) Console.WriteLine("True"); else Console.WriteLine("False"); // Prints different results. // C# 2008: False // C# 2010: True
重载解决方法。 C# 2010 会选取具有满足变体规则的泛型参数的方法,而 C# 2008 始终选择非泛型参数。
class Program { public static void Test(IEnumerable e) { Console.WriteLine("IEnumerable"); } public static void Test(IEnumerable<object> e) { Console.WriteLine("IEnumerable<object>"); } static void Main(string[] args) { Test(new List<string>()); // Prints different results. // C# 2008: IEnumerable // C# 2010: IEnumerable<object> } }
有关更多信息,请参见协变和逆变(C# 和 Visual Basic)。
Null 合并运算符
Null 合并运算符 (??) 不允许使用未赋值的局部变量。
在 C# 2010 中,即使左侧的操作数保证不为 null,也不能使用未赋值的局部变量作为 null 合并运算符右侧的操作数。
例如,以下代码可在 C# 2008 中编译,但会在 C# 2010 中生成编译器错误 CS0165。
int? i; int? j; int? x = (i = 2) ?? j;
方法组类型推理
编译器可为方法组推理泛型和非泛型委托,这会带来不确定性。
在 C# 2008 中,编译器无法为方法组推断泛型委托。 因此,它始终使用非泛型委托(如果存在)。
在 C# 2010 中,会为方法组推断泛型和非泛型委托,编译器推断两者的可能性相等。 如果同时具有某一委托的泛型和非泛型版本并且两者都满足要求,则可能带来不确定性。 例如,下面的代码可在 C# 2008 中编译并且会调用使用非泛型委托的方法。 在 C# 2010 中,此代码会生成报告调用不明确的编译器错误。
public class Sample { delegate string NonGenericDelegate(); delegate T GenericDelegate<T>(); string UseDelegate(NonGenericDelegate del) { return null; } T UseDelegate<T>(GenericDelegate<T> del) { return default(T); } public string Test() { // This line produces // a compiler error in C# 2010. return UseDelegate(Test); } }
可选参数
现在,C# 可识别 OptionalAttribute,这可能导致方法重载解决方法发生更改。
在 C# 2008 中,由于 C# 不支持可选参数,因此编译器会忽略 OptionalAttribute。
C# 2010 引入了可选参数。 还可以通过使用新语言语法或使用 OptionalAttribute 来声明可选参数。 如果在 C# 2008 中将 OptionalAttribute 用于与其他支持可选参数的语言(例如,Visual Basic)进行互操作,C# 2008 将始终只选择在方法调用中列出所有参数的方法。 C# 2010 可能会选取具有可选参数的方法,即使方法调用中未指定这些参数。
下面的代码在 C# 2008 会从基类调用方法,因为忽略了可选特性,并且编译器会假定派生类中的方法始终需要字符串参数。 在 C# 2010 中,代码从派生类中调用方法,因为此方法签名现在与方法调用匹配。
class Program { public static void Main(string[] args) { var obj = new Derived(); obj.Method(); } } class Base { public void Method() { Console.WriteLine( "Base class + no optional parameters"); } } class Derived : Base { public void Method( [Optional][DefaultParameterValue("Hello")] string s) { Console.WriteLine( "Derived class + an optional parameter"); } } // Prints different results. // C# 2008: Base class + no optional parameters // C# 2010: Derived class + an optional parameter
有关更多信息,请参见命名实参和可选实参(C# 编程指南)。
嵌入的互操作类型
如果尝试使用 CoClass 创建嵌入的 COM 类型的实例,则会导致编译器错误。
在 C# 2010 中添加对互操作程序集(如 Microsoft.Office.Interop.Word 或 Microsoft.Office.Interop.Excel)的引用时,将嵌入此程序集中的类型。 有关更多信息,请参见演练:嵌入托管程序集中的类型(C# 和 Visual Basic)和 /link(C# 编译器选项)。
当在代码中创建嵌入 COM 类型的实例时,必须使用适当的接口创建该实例。 如果尝试使用 CoClass 创建嵌入 COM 类型的实例,则编译器会报告错误。
// Add the following statement // at the beginning of the file: // using Word = Microsoft.Office.Interop.Word; // This statement does not compile in C# 2010. Word.Application wordClass = new Word.ApplicationClass(); // Use the following code instead. Word.Application wordInterface = new Word.Application();
通过 get_ 和 set_ 方法无法访问索引属性。
嵌入 COM 类型时,将动态调度对 COM 对象的所有调用。 如下面的代码示例所示,如果尝试通过 get_Range 方法访问索引属性 Range,则 C# 运行时绑定器将在类中查找用户定义的 get_Range 方法,而此方法并不存在。 若要避免此问题,请对索引属性使用 C# 2010 语法。 有关更多信息,请参见如何:通过使用 Visual C# 2010 功能访问 Office 互操作对象(C# 编程指南)。
// Add the following statement // at the beginning of the file: // using Excel = Microsoft.Office.Interop.Excel; Excel.Application excelApp = new Excel.Application(); excelApp.Visible = true; excelApp.Workbooks.Add( Excel.XlWBATemplate.xlWBATWorksheet); Excel.Worksheet sheet = excelApp.ActiveSheet as Excel.Worksheet; // The following statement throws // a run-time excpetion in C# 2010. Excel.Range rangeOld = sheet.get_Range( sheet.Cells[1, 1], sheet.Cells[2, 2]); // Use the following syntax instead. Excel.Range rangeNew = sheet.Range[sheet.Cells[1, 1], sheet.Cells[2, 2]];
事件同步
现在,使用 CompareExchange 方法可实现在编译器生成的添加和删除方法中,同步向事件的支持字段的写入。 这可能会导致争用条件。
在 C# 2010 中,通过使用 CompareExchange 方法代替 MethodImplAttribute,可同步对编译器生成的添加和删除方法的支持字段的更改。
这可能导致 C# 2008 中不存在的争用条件,如下面的代码示例中所示。
using System; using System.Threading; class Sample { public event Action sampleEvent; static void Main() { new Sample().Loop(); } void Loop() { new Thread(() => Test.Method(this)).Start(); while (true) { lock (this) { if (sampleEvent != null) { // In C# 2010, sampleEvent // can be null here, // which causes // a run-time exception. sampleEvent(); } } } } } class Test { public static void Method(Sample arg) { while (true) { arg.sampleEvent += Method; arg.sampleEvent -= Method; } } static void Method() { } }
若要避免争用条件,请修改 Loop 方法,如下面的代码示例所示。
void Loop() { new Thread(() => Test.Method(this)).Start(); while (true) { lock (this) { // Create a local copy of the delegate. Action local = sampleEvent; if (local != null) { local(); } } } }
Visual C# 入门
MSBuild