在对接口进行编程时,我发现我正在作大量的转换或对象类型转换。 编程
这两种转换方法之间有区别吗? 若是是这样,是否存在成本差别,或者这对个人计划有何影响? 安全
public interface IMyInterface { void AMethod(); } public class MyClass : IMyInterface { public void AMethod() { //Do work } // Other helper methods.... } public class Implementation { IMyInterface _MyObj; MyClass _myCls1; MyClass _myCls2; public Implementation() { _MyObj = new MyClass(); // What is the difference here: _myCls1 = (MyClass)_MyObj; _myCls2 = (_MyObj as MyClass); } }
另外,“通常”是什么首选方法? dom
看一下这些连接: 性能
他们向您展现了一些细节和性能测试。 测试
请忽略乔恩·斯凯特(Jon Skeet)的建议,从新:避免采用测试和广播模式,即: 优化
if (randomObject is TargetType) { TargetType foo = randomObject as TargetType; // Do something with foo }
花费比强制转换和null测试高的想法是错误的: spa
TargetType convertedRandomObject = randomObject as TargetType; if (convertedRandomObject != null) { // Do stuff with convertedRandomObject }
这是一个微优化,不起做用。 我运行了一些真实的测试 ,而且test-cast的速度实际上比cast-and-null的比较快,并且它也更安全,由于在if是否应该进行强制类型转换的范围内,您不可能有null引用失败。 pwa
若是您想知道试播法更快或更慢的缘由,则有一个简单而复杂的缘由。 code
简单:即便是天真的编译器也能够将两个相似的操做(如测试和投射)合并到一个测试和分支中。 cast-and-null-test能够强制执行两个测试和一个分支,一个用于类型测试并在失败时转换为null,一个用于null检查自己。 至少,它们都将针对单个测试和分支进行优化,所以测试和发布不会比强制测试和无效测试更快或更慢。 orm
复杂:为何测试和强制转换会更快:强制测试和空检测将另外一个变量引入了外部范围,编译器必须跟踪该变量的活动性,而且视控件的复杂程度而定,它可能没法优化该变量,流是。 相反,“测试和广播”仅在定界范围内引入了新变量,所以编译器知道该变量在范围退出后已死,所以能够更好地优化寄存器分配。
所以,请让这个“ cast-and-null-test优于test-and-cast”建议DIE。 请。 测试和发布既安全又快捷。
个人答案只是关于速度,若是咱们不检查类型而且在转换后不检查null的状况下。 我在Jon Skeet的代码中添加了两个附加测试:
using System; using System.Diagnostics; class Test { const int Size = 30000000; static void Main() { object[] values = new object[Size]; for (int i = 0; i < Size; i++) { values[i] = "x"; } FindLengthWithIsAndCast(values); FindLengthWithIsAndAs(values); FindLengthWithAsAndNullCheck(values); FindLengthWithCast(values); FindLengthWithAs(values); Console.ReadLine(); } static void FindLengthWithIsAndCast(object[] values) { Stopwatch sw = Stopwatch.StartNew(); int len = 0; foreach (object o in values) { if (o is string) { string a = (string)o; len += a.Length; } } sw.Stop(); Console.WriteLine("Is and Cast: {0} : {1}", len, (long)sw.ElapsedMilliseconds); } static void FindLengthWithIsAndAs(object[] values) { Stopwatch sw = Stopwatch.StartNew(); int len = 0; foreach (object o in values) { if (o is string) { string a = o as string; len += a.Length; } } sw.Stop(); Console.WriteLine("Is and As: {0} : {1}", len, (long)sw.ElapsedMilliseconds); } static void FindLengthWithAsAndNullCheck(object[] values) { Stopwatch sw = Stopwatch.StartNew(); int len = 0; foreach (object o in values) { string a = o as string; if (a != null) { len += a.Length; } } sw.Stop(); Console.WriteLine("As and null check: {0} : {1}", len, (long)sw.ElapsedMilliseconds); } static void FindLengthWithCast(object[] values) { Stopwatch sw = Stopwatch.StartNew(); int len = 0; foreach (object o in values) { string a = (string)o; len += a.Length; } sw.Stop(); Console.WriteLine("Cast: {0} : {1}", len, (long)sw.ElapsedMilliseconds); } static void FindLengthWithAs(object[] values) { Stopwatch sw = Stopwatch.StartNew(); int len = 0; foreach (object o in values) { string a = o as string; len += a.Length; } sw.Stop(); Console.WriteLine("As: {0} : {1}", len, (long)sw.ElapsedMilliseconds); } }
结果:
Is and Cast: 30000000 : 88 Is and As: 30000000 : 93 As and null check: 30000000 : 56 Cast: 30000000 : 66 As: 30000000 : 46
不要像我同样专一于速度,由于全部这些都很是快。
除了这里已经介绍的全部内容外,我还发现了一个我认为值得注意的实际区别,即显式强制转换
var x = (T) ...
与使用as
运算符相比。
这是示例:
class Program { static void Main(string[] args) { Console.WriteLine(GenericCaster<string>(12345)); Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null"); Console.WriteLine(GenericCaster<double>(20.4)); //prints: //12345 //null //20.4 Console.WriteLine(GenericCaster2<string>(12345)); Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null"); //will not compile -> 20.4 does not comply due to the type constraint "T : class" //Console.WriteLine(GenericCaster2<double>(20.4)); } static T GenericCaster<T>(object value, T defaultValue = default(T)) { T castedValue; try { castedValue = (T) Convert.ChangeType(value, typeof(T)); } catch (Exception) { castedValue = defaultValue; } return castedValue; } static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class { T castedValue; try { castedValue = Convert.ChangeType(value, typeof(T)) as T; } catch (Exception) { castedValue = defaultValue; } return castedValue; } }
底线: GenericCaster2不适用于结构类型。 GenericCaster将。
若是使用针对.NET Framework 4.X的Office PIA,则应使用as关键字,不然它将没法编译。
Microsoft.Office.Interop.Outlook.Application o = new Microsoft.Office.Interop.Outlook.Application(); Microsoft.Office.Interop.Outlook.MailItem m = o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Microsoft.Office.Interop.Outlook.MailItem;
定位.NET 2.0时, 投射能够:
Microsoft.Office.Interop.Outlook.MailItem m = (Microsoft.Office.Interop.Outlook.MailItem)o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
以.NET 4.X为目标时,错误为:
错误CS0656:缺乏编译器所需的成员'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
错误CS0656:缺乏编译器所需的成员'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'