在编程接口时,我发现我做了很多强制转换或对象类型转换。
这两种转换方法有什么区别吗?如果是的话,是否有成本差异,或者这对我的程序有什么影响?
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);
}
}
另外,“一般来说”首选的方法是什么?
OP的问题仅限于特定的选角情况。标题涵盖了更多的情况。
以下是我目前能想到的所有相关选角情况的概述:
private class CBase
{
}
private class CInherited : CBase
{
}
private enum EnumTest
{
zero,
one,
two
}
private static void Main (string[] args)
{
//########## classes ##########
// object creation, implicit cast to object
object oBase = new CBase ();
object oInherited = new CInherited ();
CBase oBase2 = null;
CInherited oInherited2 = null;
bool bCanCast = false;
// explicit cast using "()"
oBase2 = (CBase)oBase; // works
oBase2 = (CBase)oInherited; // works
//oInherited2 = (CInherited)oBase; System.InvalidCastException
oInherited2 = (CInherited)oInherited; // works
// explicit cast using "as"
oBase2 = oBase as CBase;
oBase2 = oInherited as CBase;
oInherited2 = oBase as CInherited; // returns null, equals C++/CLI "dynamic_cast"
oInherited2 = oInherited as CInherited;
// testing with Type.IsAssignableFrom(), results (of course) equal the results of the cast operations
bCanCast = typeof (CBase).IsAssignableFrom (oBase.GetType ()); // true
bCanCast = typeof (CBase).IsAssignableFrom (oInherited.GetType ()); // true
bCanCast = typeof (CInherited).IsAssignableFrom (oBase.GetType ()); // false
bCanCast = typeof (CInherited).IsAssignableFrom (oInherited.GetType ()); // true
//########## value types ##########
int iValue = 2;
double dValue = 1.1;
EnumTest enValue = EnumTest.two;
// implicit cast, explicit cast using "()"
int iValue2 = iValue; // no cast
double dValue2 = iValue; // implicit conversion
EnumTest enValue2 = (EnumTest)iValue; // conversion by explicit cast. underlying type of EnumTest is int, but explicit cast needed (error CS0266: Cannot implicitly convert type 'int' to 'test01.Program.EnumTest')
iValue2 = (int)dValue; // conversion by explicit cast. implicit cast not possible (error CS0266: Cannot implicitly convert type 'double' to 'int')
dValue2 = dValue;
enValue2 = (EnumTest)dValue; // underlying type is int, so "1.1" beomces "1" and then "one"
iValue2 = (int)enValue;
dValue2 = (double)enValue;
enValue2 = enValue; // no cast
// explicit cast using "as"
// iValue2 = iValue as int; error CS0077: The as operator must be used with a reference type or nullable type
}
请忽略Jon Skeet的建议,re:避免测试-强制转换模式,即:
if (randomObject is TargetType)
{
TargetType foo = randomObject as TargetType;
// Do something with foo
}
认为这比强制转换和空测试花费更多的想法是错误的:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
这是一种不起作用的微观优化。我运行了一些实际的测试,测试-强制转换实际上比强制转换-空比较更快,而且更安全,因为如果强制转换失败,则不可能在if之外的作用域中有空引用。
如果您想知道为什么测试-强制转换更快,或者至少不会更慢,有一个简单而复杂的原因。
简单:即使是简单的编译器也会将两个类似的操作(如test-and-cast)合并为一个测试和分支。强制转换-空测试可能强制执行两个测试和一个分支,一个用于类型测试和失败时转换为空,一个用于空检查本身。至少,它们都将优化为单个测试和分支,因此测试-强制转换既不会比强制转换-空测试慢也不会快。
复杂:为什么测试-强制转换更快:强制转换-空测试将另一个变量引入到外部作用域,编译器必须实时跟踪这个变量,并且它可能无法优化掉这个变量,这取决于你的控制流有多复杂。相反,“测试-强制转换”只在分隔的作用域内引入新变量,这样编译器就知道该变量在作用域退出后失效,从而更好地优化寄存器分配。
所以,请让这个“强制转换-空测试比测试-强制转换更好”的建议死掉吧。请。测试-强制转换既安全又快速。
这是另一个答案,带有一些IL比较。考虑这个类:
public class MyClass
{
public static void Main()
{
// Call the 2 methods
}
public void DirectCast(Object obj)
{
if ( obj is MyClass)
{
MyClass myclass = (MyClass) obj;
Console.WriteLine(obj);
}
}
public void UsesAs(object obj)
{
MyClass myclass = obj as MyClass;
if (myclass != null)
{
Console.WriteLine(obj);
}
}
}
现在看看每种方法产生的IL。即使操作码对您没有任何意义,您也可以看到一个主要的区别-在DirectCast方法中,isinst被调用后是castclass。所以基本上是两次调用而不是一次。
.method public hidebysig instance void DirectCast(object obj) cil managed
{
// Code size 22 (0x16)
.maxstack 8
IL_0000: ldarg.1
IL_0001: isinst MyClass
IL_0006: brfalse.s IL_0015
IL_0008: ldarg.1
IL_0009: castclass MyClass
IL_000e: pop
IL_000f: ldarg.1
IL_0010: call void [mscorlib]System.Console::WriteLine(object)
IL_0015: ret
} // end of method MyClass::DirectCast
.method public hidebysig instance void UsesAs(object obj) cil managed
{
// Code size 17 (0x11)
.maxstack 1
.locals init (class MyClass V_0)
IL_0000: ldarg.1
IL_0001: isinst MyClass
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0010
IL_000a: ldarg.1
IL_000b: call void [mscorlib]System.Console::WriteLine(object)
IL_0010: ret
} // end of method MyClass::UsesAs
isinst关键字与castclass的对比
这篇博文对这两种方法进行了比较。他的总结是:
在直接比较中,isinst比castclass快(尽管只有一点点)
当必须执行检查以确保转换成功时,isinst明显比castclass快
不应该使用isinst和castclass的组合,因为这比最快的“安全”转换慢得多(慢12%以上)
我个人总是使用a,因为它易于阅读,并且是。net开发团队(或者Jeffrey Richter)推荐的。
如果您使用针对。net Framework 4的Office PIAs。X你应该使用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的误差为:
缺少编译器需要的成员'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
缺少编译器需要的成员'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'