请考虑以下代码:
void Handler(object o, EventArgs e)
{
// I swear o is a string
string s = (string)o; // 1
//-OR-
string s = o as string; // 2
// -OR-
string s = o.ToString(); // 3
}
三种类型的铸造之间有什么区别(好吧,第三种不是铸造,但你得到了意图)。应该首选哪一个?
答案 0 :(得分:773)
string s = (string)o; // 1
如果o
不是string
,则会InvalidCastException。否则,即使o
为s
,也会将o
分配给null
。
string s = o as string; // 2
如果null
不是s
或o
是string
,则o
会分配给null
。因此,您不能将它与值类型一起使用(在这种情况下,运算符永远不会返回null
)。否则,将o
分配给s
。
string s = o.ToString(); // 3
如果o
为null
,则会导致NullReferenceException。无论o.ToString()
是什么类型,都会向s
分配o
次返回。
使用1进行大多数转换 - 它简单明了。我倾向于几乎从不使用2,因为如果某些东西不是正确的类型,我通常会发生异常。我只看到需要这种返回null类型的功能,其中设计糟糕的库使用错误代码(例如,返回null =错误,而不是使用异常)。
3不是强制转换,只是一个方法调用。当您需要非字符串对象的字符串表示时使用它。
答案 1 :(得分:322)
string s = (string)o;
在应有的时候使用
绝对是另一回事。 string s = o as string;
当可能是另一个时使用
事情。 string s = o.ToString();
当你不在乎什么时使用
它只是你想用的
可用的字符串表示。答案 2 :(得分:26)
这实际取决于你是否知道o
是否是一个字符串以及你想用它做什么。如果你的评论意味着o
确实是一个字符串,我更喜欢直接(string)o
演员 - 它不太可能失败。
使用直接演员的最大优点是,当它失败时,你会得到一个InvalidCastException,它会告诉你几乎出了什么问题。
使用as
运算符,如果o
不是字符串,s
设置为null
,如果您不确定并想要测试,这很方便s
:
string s = o as string;
if ( s == null )
{
// well that's not good!
gotoPlanB();
}
但是,如果您不执行该测试,则稍后将使用s
并抛出NullReferenceException。这些往往更常见,并且一旦它们在野外发生就很难追踪很多,因为几乎每一行都取消引用一个变量并可能抛出一个变量。另一方面,如果您正在尝试强制转换为值类型(任何基元或结构,例如DateTime),则必须使用直接投射 - as
将不起作用。
在转换为字符串的特殊情况下,每个对象都有一个ToString
,因此如果o
不为空并且您认为ToString
方法,则第三种方法可能没问题可能会做你想做的事。
答案 3 :(得分:9)
如果你已经知道它可以投射到什么类型,请使用C风格的演员:
var o = (string) iKnowThisIsAString;
请注意,只有使用C风格的演员才能执行显式类型强制。
如果您不知道它是否是所需类型,并且您将使用它,请使用作为关键字:
var s = o as string;
if (s != null) return s.Replace("_","-");
//or for early return:
if (s==null) return;
请注意, as 不会调用任何类型转换运算符。如果对象不为null并且本身具有指定类型,则它将仅为非null。
使用ToString()获取任何对象的人类可读字符串表示形式,即使它不能转换为字符串。
答案 4 :(得分:7)
当您使用FindControl方法时,as.net中的as关键字很好。
Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
...
}
这意味着您可以对输入的变量进行操作,而不必像使用直接强制转换那样从object
投射它:
object linkObj = this.FindControl("linkid");
if (link != null)
{
Hyperlink link = (Hyperlink)linkObj;
}
这不是一件大事,但它保存了代码行和变量赋值,而且它更具可读性
答案 5 :(得分:6)
'as'基于'is',这是一个关键字,在运行时检查对象是否是polimorphycally兼容的(基本上如果可以进行强制转换),如果检查失败则返回null。
这两个是等价的:
使用'as':
string s = o as string;
使用'是':
if(o is string)
s = o;
else
s = null;
相反,c风格的强制转换也是在运行时进行的,但如果无法进行强制转换,则抛出异常。
只是添加一个重要的事实:
'as'关键字仅适用于引用类型。你做不到:
// I swear i is an int
int number = i as int;
在这些情况下,你必须使用铸造。
答案 6 :(得分:5)
2对于转换为派生类型非常有用。
假设 a 是动物:
b = a as Badger;
c = a as Cow;
if (b != null)
b.EatSnails();
else if (c != null)
c.EatGrass();
将获得 a 以最少的演员阵容。
答案 7 :(得分:4)
“(string)o”将导致InvalidCastException,因为没有直接强制转换。
“o as string”将导致s为空引用,而不是抛出异常。
“o.ToString()”不是任何类型的演员,它是一个由对象实现的方法,因此以某种方式由.net中的每个类实现“做某事”它被调用的类的实例并返回一个字符串。
不要忘记,为了转换为字符串,还有Convert.ToString(someType instanceOfThatType),其中someType是一组类型之一,基本上是框架基类型。
答案 8 :(得分:4)
根据此页面上的实验运行:http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(这个页面有时出现一些“非法推荐人”错误,所以只要刷新就可以了)
结论是,“as”运算符通常比强制转换更快。有时快几倍,有时只是快一点。
我在peronsonally事情“as”也更具可读性。
所以,既然它既快又“更安全”(不会抛出异常),并且可能更容易阅读,我建议一直使用“as”。
答案 9 :(得分:3)
string s = o as string; // 2
是首选,因为它避免了双重铸造的性能损失。
答案 10 :(得分:3)
所有给出的答案都很好,如果我可以添加一些东西: 要直接使用字符串的方法和属性(例如ToLower),您不能写:
(string)o.ToLower(); // won't compile
你只能写:
((string)o).ToLower();
但你可以写一下:
(o as string).ToLower();
as
选项更具可读性(至少在我看来)。
答案 11 :(得分:3)
看来他们俩在概念上是不同的。
直接投射
类型不必严格关联。它有各种口味。
感觉对象将被转换为其他东西。
AS运营商
类型有直接关系。如:
感觉就像你要以不同的方式处理对象。
样本和IL
TextBlock
答案 12 :(得分:1)
我想引起注意 as 运算符的以下细节:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as
请注意,as运算符仅执行参考转换, 可为空的转换和装箱转换。 as运算符不能 执行其他转换,例如用户定义的转换, 而是应使用强制转换表达式执行。
答案 13 :(得分:0)
当试图获取任何可能为null的任何东西(任何类型)的字符串表示时,我更喜欢下面的代码行。它是紧凑的,它调用ToString(),并正确处理空值。如果o为null,则s将包含String.Empty。
String s = String.Concat(o);
答案 14 :(得分:0)
由于没有人提及它,因此最接近于关键字的instanceOf到Java:
obj.GetType().IsInstanceOfType(otherObj)
答案 15 :(得分:0)
如果您的应用string s = (string) o;
的逻辑上下文是唯一有效的类型,请使用直接投射string
。通过这种方法,您将获得InvalidCastException
并实施Fail-fast的原则。如果使用as
运算符,将保护您的逻辑不会进一步传递无效类型或获取NullReferenceException。
如果逻辑需要多个不同类型的投射string s = o as string;
并在null
上检查或使用is
运算符。
新的酷炫功能已经出现在C#7.0中,以简化演员和检查是Pattern matching:
if(o is string s)
{
// Use string variable s
}
or
switch (o)
{
case int i:
// Use int variable i
break;
case string s:
// Use string variable s
break;
}
答案 16 :(得分:0)
C#支持以下两种类型的转换(强制转换):
|
(C)v
•将给定表达式
中的静态类型v转换为c•仅当v的动态类型为c或c
的子类型时才可能•如果不是,则抛出InvalidCastException
|
v为C
•(c)v
的非致命变体•因此,将静态类型v转换为给定表达式中的c
•如果v的动态类型不是c或c
的子类型,则返回null