题
考虑下列代码:
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
}
之间有什么区别的三类型的铸件(好的,3个是不是一个铸造的,但是你得到的意向).其中一个应该是优选的?
解决方案
string s = (string)o; // 1
如果 o
是 InvalidCastException ,则会抛出不是字符串
。否则,将 o
分配给 s
,即使 o
是 null
。
string s = o as string; // 2
如果 o
不是字符串
或 o
将 null
分配给 s
>是 null
。因此,您不能将它与值类型一起使用(在这种情况下,运算符永远不会返回 null
)。否则,将 o
分配给 s
。
string s = o.ToString(); // 3
如果 o
,则会导致 NullReferenceException 是 null
。分配任何 o.ToString()
返回 s
,无论 o
是什么类型。
使用1进行大多数转换 - 它简单明了。我倾向于几乎从不使用2,因为如果某些东西不是正确的类型,我通常会发生异常。我只看到需要这种返回null类型的功能,其中设计糟糕的库使用错误代码(例如,返回null =错误,而不是使用异常)。
3不是强制转换,只是一个方法调用。当您需要非字符串对象的字符串表示时使用它。
其他提示
-
string s =(string)o;
什么时候应该使用 绝对是另一回事。 -
string s = o as string;
当可能其他内容时使用 事情。 -
string s = o.ToString();
当你不关心什么时使用 它只是你想用的 可用的字符串表示。
醇>
这实际取决于你是否知道 o
是否是一个字符串以及你想用它做什么。如果你的评论意味着 o
真的是一个字符串,我更喜欢直接的(string)o
cast - 它不太可能失败。
使用直接强制转换的最大优点是,当它失败时,你得到一个 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
不为null,你认为第三种方法可能没问题 ToString
方法可能会做你想要的。
如果您已经知道它可以投射到什么类型,请使用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()获取任何对象的人类可读字符串表示形式,即使它不能转换为字符串。
当您使用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;
}
这不是一件大事,但它保存了代码行和变量赋值,而且它更具可读性
'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;
在这些情况下,你必须使用铸造。
2对于转换为派生类型非常有用。
假设 a 是动物:
b = a as Badger;
c = a as Cow;
if (b != null)
b.EatSnails();
else if (c != null)
c.EatGrass();
将获得 a 以最少的演员阵容。
根据本页运行的实验: http:/ /www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(这个页面有时会出现一些“非法推荐人”错误,所以只要刷新就可以了)
结论是,“as”操作员通常比演员更快。有时快几倍,有时只是快一点。
我是个人的事情,因为“也更具可读性。
因此,它既快又“更安全”。 (不会抛出异常),并且可能更容易阅读,我建议使用“as”;每时每刻。
"(字符串)O"将导致InvalidCastException,因为没有直接强制转换。
" o as string"将导致s为空引用,而不是抛出异常。
" o.ToString()"它不是任何类型的演员,它是一个由对象实现的方法,因此以某种方式由.net中的每个类“执行某些操作”实现。与调用它的类的实例并返回一个字符串。
不要忘记,为了转换为字符串,还有Convert.ToString(someType instanceOfThatType),其中someType是一组类型之一,基本上是框架基类型。
所有给出的答案都很好,如果我可以添加一些东西: 要直接使用字符串的方法和属性(例如ToLower),您不能写:
(string)o.ToLower(); // won't compile
你只能写:
((string)o).ToLower();
但你可以写一下:
(o as string).ToLower();
as
选项更具可读性(至少在我看来)。
string s = o as string; // 2
是首选,因为它避免了双重铸造的性能损失。
它似乎他们两个在概念上是不同的。
直接铸造
类型不需要严格相关。它进来所有类型的口味。
- 定义隐含/明确的铸造: 通常一个新的目的是创建。
- 值型隐: 复制而不丢失信息。
- 值类型明确的: 复制和信息可能丢失。
- 是-的关系: 更改基准类型,否则抛出例外。
- 同的类型: '铸是多余的'.
这感觉就像的目的是要被转换成别的东西。
如操作者
类有直接的关系。为:
- 参照类型: 是的一种关系 对象总是一样的,只是参考的变化。
- 值的类型: 复制 拳击和空类型.
这感觉就像你要处理的对象在一个不同的方式。
样品和IL
class TypeA
{
public int value;
}
class TypeB
{
public int number;
public static explicit operator TypeB(TypeA v)
{
return new TypeB() { number = v.value };
}
}
class TypeC : TypeB { }
interface IFoo { }
class TypeD : TypeA, IFoo { }
void Run()
{
TypeA customTypeA = new TypeD() { value = 10 };
long longValue = long.MaxValue;
int intValue = int.MaxValue;
// Casting
TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL: call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass ConsoleApp1.Program/IFoo
int loseValue = (int)longValue; // explicit -- IL: conv.i4
long dontLose = intValue; // implict -- IL: conv.i8
// AS
int? wraps = intValue as int?; // nullable wrapper -- IL: call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo
//TypeC d = customTypeA as TypeC; // wouldn't compile
}
我想提请注意 as 运算符的以下细节:
https://docs.microsoft.com / EN-US / DOTNET / CSHARP /语言参考/关键字/作为
请注意,as运算符仅执行引用转换, 可空的转换和拳击转换。 as运营商不能 执行其他转换,例如用户定义的转换 应该使用强制转换表达式来执行。
当尝试获取可能为null的任何(任何类型)的字符串表示时,我更喜欢下面的代码行。它是紧凑的,它调用ToString(),并正确处理空值。如果o为null,则s将包含String.Empty。
String s = String.Concat(o);
由于没有人提及它,因此最接近于关键字的instanceOf到Java:
obj.GetType().IsInstanceOfType(otherObj)
使用直接强制转换字符串s =(字符串)o;
如果在应用程序的逻辑上下文中 string
是唯一有效的类型。使用这种方法,您将获得 InvalidCastException
并实现失败的原则 - 快速。如果使用作为
运算符,将保护您的逻辑不会进一步传递无效类型或获取NullReferenceException。
如果逻辑需要几个不同的类型,将 string s = o转换为字符串;
并在 null
上检查它,或者使用是
运算符。
新的酷炫功能已经出现在C#7.0中以简化演员和检查是模式匹配:
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;
}
C#支持以下两种类型的转换(强制转换):
|
(C)v
&#8226;在给定表达式
中将v的静态类型转换为c&#8226;仅当v的动态类型为c或c
的子类型时才可能&#8226;如果不是,则抛出InvalidCastException
|
v为C
&#8226; (c)v
的非致命变体&#8226;因此,在给定表达式
中将v的静态类型转换为c&#8226;如果v的动态类型不是c或c
的子类型,则返回null