直接铸造与“ as”运算符?


709

考虑以下代码:

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
}

三种类型的转换之间有什么区别(好吧,第三个类型不是转换,但您有意图)。应该首选哪一个?


1
并非完全重复,但在上一个问题中也进行了一些性能讨论。
切片

8
第4名:string s = Convert.ToString(o); 第五名:(string s = $"{o}"或等效string.Format于早期C#的形式)
Earth Engine

Answers:


833
string s = (string)o; // 1

如果不是,则抛出InvalidCastException。否则,将分配给,即使是。ostringosonull

string s = o as string; // 2

分配nullsif o不是a string或if ois null。因此,您不能将其与值类型一起使用(null在这种情况下,运算符永远不会返回)。否则,分配os

string s = o.ToString(); // 3

导致一个NullReferenceException异常,如果onullo.ToString()将返回值赋给s,无论是哪种类型o


大多数转换使用1-简单明了。我倾向于几乎从不使用2,因为如果某些类型不正确,我通常希望发生异常。我只看到这种返回null类型的功能需要使用错误代码(例如,返回null =错误,而不是使用异常)的错误设计的库。

3不是强制转换,而只是方法调用。在需要非字符串对象的字符串表示形式时使用它。


2
您可以在明确定义时将'null'赋给值类型,例如:int?一世; 字符串s =“ 5”; i = s为整数;//我现在是5 s = null; i = s为整数;//我现在为空
Anheledir

3
RE:Anheledir实际上我在第一个电话之后将为空。您必须使用显式转换函数来获取字符串的值。
Guvante's

45
RE:Sander实际上,还有另一个很好的理由使用它,它简化了您的检查代码(检查空值,然后检查空值和正确的类型),这很有用,因为很多时候您宁愿抛出一个自定义的异常。但是,确实如此,盲人打电话是不好的。
Guvante's

5
#2对于不知道输入类型的Equals方法之类的东西很方便,不过通常是1,尽管比这更可取,但是当您只期望一种类型时,显然会使用类型系统将其限制为一种类型:)
Calum

6
当您的代码可能针对特定类型执行某些操作但不执行任何操作时,#2也很有用。
AnthonyWJones

349
  1. string s = (string)o;使用时要的东西 肯定是另一件事。
  2. string s = o as string;当某事可能是另一回事时使用。
  3. string s = o.ToString(); 当您不在乎它是什么但只想使用可用的字符串表示形式时使用。

1
我觉得这个答案听起来不错,但可能不正确。
j riv

1
我喜欢前两个,但是我要在第三个选项中添加“并且您确定它不为空”。
Uxonith

2
您现在可以使用猫王(?。)来避免担心它:obj?.ToString()
Quibblesome

@Quibblesome-很好的答案,但是我不得不停下来考虑你的重新装瓶!从字面上让我震惊的是,这种语言已经存在了15年左右。感觉就像昨天,当我们所有人都在“说服”试图说服高级开发人员转向C#时。
Griswald_911 '18

1
@Quibblesome不错的答案:如果我添加1/2/3的值,您会感到烦恼,这样就不必向上滚动到OP。我与SO将根据投票对旧答案进行排名!
whytheq

29

这实际上取决于您是否知道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不为null 可能没问题,并且您认为该ToString方法可以完成您想要的事情。


2
一注-您可以使用as带有可空值类型。IE o as DateTime无法正常工作,但o as DateTime?可以...
John Gibb 2014年

为什么不使用它if (s is string)呢?
BornToCode 2014年

1
@BornToCode,对我而言,很大程度上是个人喜好。根据您的操作,通常在ising 之后,无论如何都必须再次进行强制转换,因此您必须先进行is然后再强制转换。由于某种原因,as和null检查对我来说感觉更好。
布莱尔·康拉德

9

如果您已经知道它可以转换为哪种类型,请使用C样式的转换:

var o = (string) iKnowThisIsAString; 

请注意,只有使用C样式强制转换,您才能执行显式类型强制。

如果您不知道它是否是所需的类型,并且打算使用它,请使用as关键字:

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

注意,as不会调用任何类型转换运算符。仅当对象不为null且本身是指定类型的对象时,它才会为非null。

使用ToString()获取任何对象的人类可读的字符串表示形式,即使它不能转换为字符串也是如此。


3
关于类型转换运算符,这是一个有趣的小陷阱。我为转换创建了一些类型,然后必须注意这一点。
AnthonyWJones

7

当您使用FindControl方法时,as关键字在asp.net中很好。

Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
     ...
}

这意味着您可以对类型化的变量进行操作,而不必object像直接强制转换那样将其强制转换为:

object linkObj = this.FindControl("linkid");
if (link != null)
{
     Hyperlink link = (Hyperlink)linkObj;
}

这不是一件大事,但它节省了代码行和变量赋值,而且可读性强


6

“ as”基于“ is”,这是一个关键字,它在运行时检查对象是否在政治上兼容(基本上可以进行强制转换),如果检查失败,则返回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;

在这种情况下,您必须使用投射。


感谢您指出我的错误,您是对的。我编辑了答案。哎呀,对不起。
Sergio Acosta

5

2对于强制转换为派生类型很有用。

假设a是动物:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

将得到一个用最少的铸件送入。


2
@Chirs Moutray,这并不总是可能的,特别是如果它是一个库。
deceleratedcaviar

5

根据在此页面上运行的实验:http : //www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(此页面有时会显示一些“非法引荐来源网址”错误,因此请刷新一下)

结论是,“ as”运算符通常比强制转换更快。有时快很多倍,有时快一点。

我个人认为“ as”也更易读。

因此,由于它既更快又更“安全”(不会抛出异常),并且可能更易于阅读,所以我建议始终使用“ as”。


4

“(string)o”将导致InvalidCastException,因为没有直接强制转换。

“ o as string”将导致s为空引用,而不是引发异常。

“ o.ToString()”本身并不是一种强制转换,它是一种由对象实现的方法,因此由.net中的每个类以某种方式对.NET的实例“做某事”实现调用它的类并返回一个字符串。

不要忘记,要转换为字符串,还有Convert.ToString(someType instanceOfThatType),其中someType是一组类型之一,本质上是框架的基本类型。


3

如果我可能要添加一些内容,那么所有给出的答案都是好的:要直接使用字符串的方法和属性(例如ToLower),您不能编写:

(string)o.ToLower(); // won't compile

您只能写:

((string)o).ToLower();

但您可以改为:

(o as string).ToLower();

as选项更具可读性(至少在我看来)。


(o作为字符串).ToLower()构造破坏了as运算符的目的。当o无法转换为字符串时,这将引发null引用异常。
詹姆斯

@james-但是谁说as运算符的唯一目的是在强制转换失败时引发异常?如果您知道 o是一个字符串,并且只想编写更简洁的代码,则可以使用(o as string).ToLower()而不是多个令人困惑的括号。
BornToCode '16

as的目的恰恰相反-强制转换失败时不应引发异常,而应返回null。假设您的o是一个值为null的字符串,那么会发生什么?提示-您的ToLower呼叫将失败。
James

@james-您是对的,但是在肯定可以确定它不会为null的情况下呢?我只需要对编译器进行强制转换即可让我访问该对象的方法?
BornToCode

1
您绝对可以这样做,但这并不是最佳做法,因为您不想依赖调用方或外部系统来确保您的值不为null。如果您使用的是C#6,则可以(以字符串形式)吗?降低()。
james

3
string s = o as string; // 2

首选,因为它避免了双重铸造的性能损失。


克里斯,您好,答案中的链接现在是404 ...我不确定您是否要替换掉它?
马特

3

看来他们两个在概念上是不同的。

直接铸造

类型不必严格相关。它有各种口味。

  • 自定义隐式/显式强制转换:通常创建一个新对象。
  • 值类型隐式:复制而不会丢失信息。
  • 值类型显式:复制和信息可能会丢失。
  • IS-A关系:更改引用类型,否则引发异常。
  • 相同类型: “铸造是多余的”。

感觉该对象将被转换成其他东西。

AS运营商

类型有直接关系。如:

  • 引用类型:IS-A关系对象总是相同的,只是引用发生了变化。
  • 值类型:复制拳击和可为空的类型。

感觉就像您将以不同的方式处理对象。

样品和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
    }


0

当试图获取任何可能为空的任何类型的字符串表示形式时,我喜欢下面的代码行。它紧凑,可以调用ToString(),并且可以正确处理null。如果o为null,则s将包含String.Empty。

String s = String.Concat(o);

0

由于没有人提到它,因此关键字最接近Java的instanceOf是:

obj.GetType().IsInstanceOfType(otherObj)

0

string s = (string) o;如果在您应用的逻辑上下文中string是唯一有效的类型,请使用直接强制转换。使用这种方法,您将获得InvalidCastException并实现Fail-fast的原理。您的逻辑将受到保护,以免进一步传递无效类型,如果使用了as运算符,则将获得NullReferenceException 。

如果逻辑期望几种不同类型的类型,string s = o as string;并进行检查null或使用is运算符。

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;
 }

0

C#支持以下两种类型的类型转换(广播)形式:

|

(简历

•在给定表达式中将v的静态类型转换为c

•仅当v的动态类型为c或c的子类型时才可能

•如果不是,则抛出InvalidCastException

|

v作为C

•(c)v的非致命变体

•因此,在给定表达式中将v的静态类型转换为c

•如果v的动态类型不是c或c的子类型,则返回null

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.