806

考虑以下代码:

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
}

这三种类型的施法有什么区别(好吧,第三种不是施法,但你明白了)。应该首选哪一个?

4

16 回答 16

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

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

string s = o as string; // 2

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

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

如果是 则导致NullReferenceException。将返回的任何内容分配给,无论是什么类型。onullo.ToString()so


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

3 不是强制转换,只是一个方法调用。当您需要非字符串对象的字符串表示时使用它。

于 2008-09-25T10:16:26.240 回答
396
  1. string s = (string)o;当某件事 绝对应该是另一件事时使用。
  2. string s = o as string;当某事可能是另一件事时使用。
  3. string s = o.ToString();当您不关心它是什么但只想使用可用的字符串表示时使用。
于 2008-09-25T10:31:28.453 回答
32

这实际上取决于您是否知道是否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不起作用。

在转换为字符串的特殊情况下,每个对象都有一个,因此如果不为 null 并且您认为该方法可能会执行您想要的操作ToString,则您的第三个方法可能没问题。oToString

于 2008-09-25T10:16:50.593 回答
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() 获取任何对象的人类可读的字符串表示,即使它不能转换为字符串。

于 2008-09-25T10:41:43.170 回答
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;
}

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

于 2008-09-25T11:17:27.030 回答
6

'as' 基于 'is',它是一个关键字,在运行时检查对象是否是多态兼容的(基本上是否可以进行强制转换),如果检查失败则返回 null。

这两个是等价的:

使用“作为”:

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;

在这些情况下,您必须使用强制转换。

于 2008-09-25T10:15:32.670 回答
6

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

(此页面有时会出现一些“非法引荐来源”错误,因此请刷新)

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

我个人认为“as”也更具可读性。

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

于 2010-08-15T18:36:54.803 回答
5

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

假设a是动物:

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

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

将获得最少的演员阵容。

于 2008-09-25T10:31:39.170 回答
4

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

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

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

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

于 2008-09-25T10:17:50.810 回答
4

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

直接铸造

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

  • 自定义隐式/显式转换:通常会创建一个新对象。
  • 值类型隐式:复制而不丢失信息。
  • 值类型显式:副本和信息可能会丢失。
  • 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
    }
于 2016-09-25T04:47:48.387 回答
3
string s = o as string; // 2

是首选,因为它避免了双重转换的性能损失。

于 2008-09-25T11:05:43.113 回答
3

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

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

你只能写:

((string)o).ToLower();

但你可以改写:

(o as string).ToLower();

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

于 2014-04-03T14:28:13.900 回答
2

我想提请注意as运算符的以下细节:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

请注意,as 运算符仅执行引用转换、可为空的转换和装箱转换。as 运算符不能执行其他转换,例如用户定义的转换,而应使用强制转换表达式来执行。

于 2018-10-17T12:11:02.687 回答
1

string s = (string) o;如果在您的应用程序的逻辑上下文中string是唯一有效的类型,请使用直接强制转换。通过这种方法,您将获得InvalidCastException并实现快速失败的原则。如果使用运算符,您的逻辑将受到保护,不会进一步传递无效类型或获取 NullReferenceException as

如果逻辑需要几种不同的类型转换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;
 }
于 2017-08-03T07:34:30.523 回答
0

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

String s = String.Concat(o);
于 2012-06-23T01:31:48.503 回答
0

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

obj.GetType().IsInstanceOfType(otherObj)
于 2015-10-13T20:21:25.407 回答