Possible Duplicate:
Casting vs using the ‘as’ keyword in the CLR
号
我最近学到了一种不同的演员阵容。而不是使用
1
| SomeClass someObject = (SomeClass) obj; |
可以使用以下语法:
1
| SomeClass someObject = obj as SomeClass; |
号
如果obj不是someclass,则返回空值,而不是抛出类强制转换异常。
我看到,如果强制转换失败,并且我试图访问someobject变量,这可能导致nullreferenceexception。所以我想知道这个方法的基本原理是什么?为什么要使用这种类型的转换而不是(旧的)类型-它似乎只会将失败的类型转换的问题"深入"到代码中。
- 相关且信息丰富:stackoverflow.com/questions/496096/…
- 关于这个问题的更多想法,请参阅我关于这个主题的文章:blogs.msdn.com/b/ericlippet/archive/2009/10/08/…
- blogs.msdn.com/b/ericlippert/archive/2009/10/08/…
对于"经典"方法,如果强制转换失败,将引发异常。使用as方法,它将导致可以检查的空值,并避免引发异常。
此外,您只能将"as"与引用类型一起使用,因此如果您要将类型转换为值类型,则必须仍然使用"classic"方法。
注:
as方法只能用于可以分配null值的类型。它只用于表示引用类型,但当.NET 2.0出现时,它引入了可为空值类型的概念。由于这些类型可以分配一个null值,因此它们可以与as运算符一起使用。
- 此外,"经典"演员表可以执行转换。as只能进行引用/装箱转换。
- 准确地说,您还可以将"as"用于Nullable类型(可空类型是值类型…)
- 不可以。可以为空的类型是引用类型,因为不能为值类型赋值为空。Nullable类是一个引用包装器,它封装了值类型,本身可以为空。
- Nullable是一个值类型(如果用reflector查看它,则声明为struct)。对于可以为空的类型,在运行时必须有一个特殊的规则,因为如果我尝试使用"as"将其强制转换为int,则编译器的确切错误是"as运算符必须与引用类型或可以为空的类型一起使用("int"是不可为空的值类型)"。
- 正如我在下面的回答中提到的,as要求类型可以赋值为null。所有引用类型都可以指定为null,如果值类型支持它(如Nullable所做),那么也可以使用它。
- 我明白你的意思,但是根据定义,一个值类型不能被指定为null,所以我假设可以为空的类型使用我喜欢称之为"黑色魔法"(即运行时支持它的特殊逻辑)。
- @BrianBall:"运行时必须有特殊的规则"-我相信它只是使用规则的隐式和显式转换操作符,所以没有特殊的规则。
- @布莱恩,@paul:没有运行时的"黑魔法"。C和VB.NET语言内置了处理Nullable的特定逻辑。与null的比较被转换成.HasValue,对空的赋值被转换成=new Nullable()(或任何合适的类型,显然)。
- @亚当:"特定的逻辑内置于处理……"——这是我对"黑魔法"的定义。它本质上是最终开发人员无法完成的,或者如果不深入了解运行时就无法完成的。
- @布莱恩:就像我说的,没有运行时的"黑色魔法"。当然有很多魔法,但它完全是编译时和特定语言的。运行时处理Nullable与处理任何其他值类型没有任何不同。
空比较比抛出和捕获异常快得多。异常具有显著的开销-必须组装堆栈跟踪等。
异常应该代表一种意外的状态,这种状态通常不代表这种情况(即当as工作得更好时)。
- 现在,开发人员不需要考虑在这种不重要的事情上什么是更快的。"堆栈跟踪必须组装"是什么意思?异常并不总是表示意外状态。FileNotFoundException是否始终处于意外状态?
- 我认为FileNotFoundException是一种意想不到的状态。您尝试使用文件执行某些操作,但不检查该文件是否首先存在,或者您确定该文件应该存在,但它意外地不存在。不?:)
- 通常的"过早优化"论点在这里适用:(a)性能敏感的代码只是代码库的一小部分,(b)在所有其他代码中,优雅(更自然地对应用程序域语义进行建模)、可读性和对测试的开放性是更重要的设计对象。艾夫斯,因为我们在这里节省了昂贵的开发人员时间,而不是组装堆栈跟踪所需的纳秒时间,等等。
在某些情况下,处理null比处理例外情况更容易。尤其是,合并运算符非常方便:
1
| SomeClass someObject = (obj as SomeClass ) ?? new SomeClass (); |
它还简化了基于对象类型的(不使用多态性和)分支的代码:
1 2 3 4 5 6 7 8 9 10
| ClassA a;
ClassB b;
if ((a = obj as ClassA) != null)
{
// use a
}
else if ((b = obj as ClassB) != null)
{
// use b
} |
号
根据msdn页面的规定,as运算符相当于:
1
| expression is type ? (type )expression : (type )null |
它完全避免了有利于更快的类型测试的异常,但也限制了它对支持null的类型(引用类型和Nullable的类型)的使用。
- 你真的觉得这些代码简化了吗?我找不到您的第一个代码有用的情况——"如果obj不是someclass类型,那么使用一个新对象"。我编写的第二个代码就好像(obj是classa)classa=(classa)obj;…-它肯定会更简单,更容易理解。
- @如果用所提供的对象不需要实现的ISomeFeature替换前两个SomeClass,则第一个示例更现实;如果不需要实现,则使用默认实现。在第二个示例中,fxcop将警告您的版本使用双重转换(性能),尽管类型测试表明设计糟糕,但是性能是第二个问题。在这种情况下,我尝试并只提供SO上的最佳实践代码,并遵循MS自己的指导原则。
- 如果((A=OBJ,A类)!=空),如果(obj是A类),则可以写入,越短越好。
as运算符在几种情况下都很有用。
当您只需要知道一个对象是特定类型的,但不需要对该类型的成员执行特定的操作时
当你想避免例外,而不是明确处理null时
您想知道对象之间是否存在CLR转换,而不仅仅是一些用户定义的转换。
第三点很微妙但很重要。没有一个1-1映射,在这两个映射之间,CAST运算符将成功执行CAST,而as运算符将成功执行CAST。as运算符严格限制为clr转换,不考虑用户定义的转换(cast运算符将)。
具体来说,as运算符仅允许以下内容(从C lang规范第7.9.11节)
- 同一性(§6.1.1)、隐式引用(§6.1.6)、拳击(§6.1.7)、显式引用(§6.2.4)或拆箱(§6.2.5)从E型转换为T型。
- E或T的类型是开放类型。
- e是空文本。
- 你的第一个病例肯定会用到is?你不应该用expr is type代替(expr as type) != null吗?
- @Chrismorgan第一种情况是指当您通过type的接受值时。这并不是为了涵盖你提到的案件(is绝对更合适)
当您真正不知道变量的类型时,as关键字很有用。如果您有一个函数将根据参数的实际类型遵循不同的代码路径,那么您有两个选择:
首先,使用普通铸件:
1 2 3 4 5 6 7 8 9 10
| if(myObj is string)
{
string value = (string)myObj ;
... do something
}
else if(myObj is MyClass )
{
MyClass = (MyClass )myObj ;
} |
。
这要求您使用is检查对象的类型,以便不尝试将其强制转换为将失败的对象。这也有点多余,因为is类型检查在强制转换中再次执行(以便在需要时抛出异常)。
另一种方法是使用as。
1 2 3 4 5 6 7 8 9 10 11
| string myString = myObj as string;
MyClass myClass = myObj as MyClass;
if(myString != null)
{
}
else if(myClass != null)
{
} |
这使得代码略短,并且消除了冗余类型检查。
我认为最好的"规则"是只在预期你的主题不会是你要投射到的对象时使用"as"关键字:
1 2 3 4 5 6 7 8 9 10 11 12
| var x = GiveMeSomething();
var subject = x as String;
if(subject != null)
{
// do what you want with a string
}
else
{
// do what you want with NOT a string
} |
。
然而,当你的主题应该是你要铸造的类型,使用"经典铸造",正如你所说。因为如果它不是您期望的类型,您将得到一个符合特殊情况的异常。
如果不是有效的强制转换,则使用as将返回空值,该强制转换允许您在try/catch中包装强制转换之外执行其他操作。我讨厌经典的演员阵容。如果我不确定,我总是用演员表。另外,例外是昂贵的。空检查不是。
我认为,如果将强制转换的结果传递给一个方法,而您知道该方法将处理空引用,而不进行抛出和ArgumentNullException或类似的操作,这是很有用的。
我发现as的用处很小,因为:
。
慢于:
1 2
| if (obj is T )
...(T )obj ... |
使用as对我来说是一个非常边缘的场景,所以我不能想到任何一般的规则,当我将它用于仅仅把(更具信息性的)强制转换异常向上扩展时。
您可以使用"as"语句来避免出现异常的可能性,例如,您可以通过逻辑优雅地处理强制转换失败。只有在确定对象是所需类型时才使用强制转换。我几乎总是使用"as",然后检查是否为空。
这里什么都没有发生。基本上,测试某个东西看它是否属于某种类型(即使用"a s")是很方便的。您需要检查"as"调用的结果,看看结果是否为空。
当您希望强制转换工作并且希望抛出异常时,请使用"Classic"方法。