考虑下列代码:

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不是强制转换,只是一个方法调用。当您需要非字符串对象的字符串表示时使用它。

其他提示

  1. string s =(string)o; 什么时候应该使用 绝对是另一回事。
  2. string s = o as string; 可能其他内容时使用 事情。
  3. 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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top