考虑以下代码:

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
}

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


当前回答

如果您已经知道它可以转换为什么类型,请使用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不会调用任何类型转换运算符。仅当对象不为空且本机为指定类型时,它才为非空。

使用ToString()获取任何对象的可读字符串表示,即使它不能转换为字符串。

其他回答

如果我可以补充一下,所有给出的答案都很好:要直接使用字符串的方法和财产(例如ToLower),您不能编写:

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

你只能写:

((string)o).ToLower();

但你可以写:

(o as string).ToLower();

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

“as”基于“is”,这是一个关键字,用于在运行时检查对象是否与polimorphyly兼容(基本上,如果可以进行强制转换),如果检查失败,则返回null。

这两者是等价的:

使用“as”:

string s = o as string;

使用“is”:

if(o is string) 
    s = o;
else
    s = null;

相反,c样式转换也在运行时进行,但如果无法进行转换,则会引发异常。

只是为了补充一个重要的事实:

“as”关键字仅适用于引用类型。您不能执行以下操作:

// I swear i is an int
int number = i as int;

在这种情况下,你必须使用铸造。

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

String s = String.Concat(o);

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

直接铸造

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

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

感觉对象将被转换为其他对象。

AS操作员

类型有直接关系。如:

引用类型:IS-A关系对象始终相同,只是引用更改。值类型:复制装箱和可为null的类型。

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

样品和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
    }
string s = (string)o; // 1

如果o不是字符串,则引发InvalidCastException。否则,将o赋给s,即使o为空。

string s = o as string; // 2

如果o不是字符串或o为空,则将null赋给s。因此,不能将其用于值类型(在这种情况下,运算符永远不会返回null)。否则,将o指定给s。

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

如果o为空,则导致NullReferenceException。无论o是什么类型,都将任何o赋值给ToString()返回的值。


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

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