简体   繁体   English

直接转换 vs 'as' 运算符?

[英]Direct casting vs 'as' operator?

Consider the following code:考虑以下代码:

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
}

What is the difference between the three types of casting (okay, the 3rd one is not a casting, but you get the intent).三种类型的铸造有什么区别(好吧,第三种不是铸造,但你明白了意图)。 Which one should be preferred?应该首选哪一个?

string s = (string)o; // 1

Throws InvalidCastException if o is not a string .如果o不是string则抛出InvalidCastException Otherwise, assigns o to s , even if o is null .否则,将o分配给s ,即使onull

string s = o as string; // 2

Assigns null to s if o is not a string or if o is null .分配nulls ,如果o是不是一个string ,或者onull For this reason, you cannot use it with value types (the operator could never return null in that case).出于这个原因,您不能将它与值类型一起使用(在这种情况下,运算符永远不会返回null )。 Otherwise, assigns o to s .否则,将o分配给s

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

Causes a NullReferenceException if o is null .如果onull则导致NullReferenceException Assigns whatever o.ToString() returns to s , no matter what type o is.o.ToString()返回的任何o.ToString()分配给s ,无论o是什么类型。


Use 1 for most conversions - it's simple and straightforward.对大多数转换使用 1 - 它简单明了。 I tend to almost never use 2 since if something is not the right type, I usually expect an exception to occur.我几乎从不使用 2,因为如果某些东西的类型不正确,我通常会期望发生异常。 I have only seen a need for this return-null type of functionality with badly designed libraries which use error codes (eg return null = error, instead of using exceptions).我只看到使用错误代码(例如,返回 null = 错误,而不是使用异常)的设计糟糕的库需要这种返回空类型的功能。

3 is not a cast and is just a method invocation. 3 不是强制转换,只是一个方法调用。 Use it for when you need the string representation of a non-string object.当您需要非字符串对象的字符串表示时使用它。

  1. string s = (string)o; Use when something should definitely be the other thing.当某事绝对应该是另一件事时使用。
  2. string s = o as string; Use when something might be the other thing.当某事可能是另一件事时使用。
  3. string s = o.ToString(); Use when you don't care what it is but you just want to use the available string representation.当您不在乎它是什么但只想使用可用的字符串表示时使用。

It really depends on whether you know if o is a string and what you want to do with it.这实际上取决于您是否知道o是否是字符串以及您想用它做什么。 If your comment means that o really really is a string, I'd prefer the straight (string)o cast - it's unlikely to fail.如果你的评论意味着o真的是一个字符串,我更喜欢直接的(string)o演员 - 它不太可能失败。

The biggest advantage of using the straight cast is that when it fails, you get an InvalidCastException , which tells you pretty much what went wrong.使用直接转换的最大优点是,当它失败时,您会得到一个InvalidCastException ,它几乎可以告诉您出了什么问题。

With the as operator, if o isn't a string, s is set to null , which is handy if you're unsure and want to test s :使用as运算符,如果o不是字符串,则s设置为null ,如果您不确定并想要测试s ,这很方便:

string s = o as string;
if ( s == null )
{
    // well that's not good!
    gotoPlanB();
}

However, if you don't perform that test, you'll use s later and have a NullReferenceException thrown.但是,如果您不执行该测试,则稍后将使用s并抛出NullReferenceException These tend to be more common and a lot harder to track down once they happens out in the wild, as nearly every line dereferences a variable and may throw one.这些往往是更常见,很多难以追查,一旦他们在野外发生了,因为几乎每行取消引用变量,可能会抛出一个。 On the other hand, if you're trying to cast to a value type (any primitive, or structs such as DateTime ), you have to use the straight cast - the as won't work.另一方面,如果您尝试转换为值类型(任何基元或结构,例如DateTime ),则必须使用直接转换 - as不起作用。

In the special case of converting to a string, every object has a ToString , so your third method may be okay if o isn't null and you think the ToString method might do what you want.在转换为字符串的特殊情况下,每个对象都有一个ToString ,因此如果o不为空并且您认为ToString方法可能会执行您想要的操作,则您的第三种方法可能没问题。

If you already know what type it can cast to, use a C-style cast:如果您已经知道它可以转换为什么类型,请使用 C 样式转换:

var o = (string) iKnowThisIsAString; 

Note that only with a C-style cast can you perform explicit type coercion.请注意,只有使用 C 风格的强制转换才能执行显式类型强制。

If you don't know whether it's the desired type and you're going to use it if it is, use as keyword:如果您不知道它是否是所需的类型并且您打算使用它,请使用as关键字:

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

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

Note that as will not call any type conversion operators.请注意as不会调用任何类型转换运算符。 It will only be non-null if the object is not null and natively of the specified type.如果对象不为 null 且本机为指定类型,则它只会为非 null。

Use ToString() to get a human-readable string representation of any object, even if it can't cast to string.使用 ToString() 获取任何对象的人类可读的字符串表示形式,即使它不能转换为字符串。

The as keyword is good in asp.net when you use the FindControl method.当您使用 FindControl 方法时, as 关键字在 asp.net 中很好。

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

This means you can operate on the typed variable rather then having to then cast it from object like you would with a direct cast:这意味着您可以对类型化变量进行操作,而不必像使用直接转换那样从object它:

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

It's not a huge thing, but it saves lines of code and variable assignment, plus it's more readable这不是什么大事,但它节省了代码行和变量赋值,而且更具可读性

'as' is based on 'is', which is a keyword that checks at runtime if the object is polimorphycally compatible (basically if a cast can be made) and returns null if the check fails. “as”基于“is”,它是一个关键字,用于在运行时检查对象是否多态兼容(基本上如果可以进行强制转换)并在检查失败时返回 null。

These two are equivalent:这两个是等价的:

Using 'as':使用“作为”:

string s = o as string;

Using 'is':使用“是”:

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

On the contrary, the c-style cast is made also at runtime, but throws an exception if the cast cannot be made.相反,c 样式的强制转换也在运行时进行,但如果无法进行强制转换则抛出异常。

Just to add an important fact:补充一个重要的事实:

The 'as' keyword only works with reference types. “as”关键字仅适用于引用类型。 You cannot do:你不能这样做:

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

In those cases you have to use casting.在这些情况下,您必须使用强制转换。

According to experiments run on this page: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as根据在此页面上运行的实验: http : //www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(this page is having some "illegal referrer" errors show up sometimes, so just refresh if it does) (此页面有时会出现一些“非法引荐来源”错误,因此如果确实如此,请刷新)

Conclusion is, the "as" operator is normally faster than a cast.结论是,“as”运算符通常比强制转换更快。 Sometimes by many times faster, sometimes just barely faster.有时快很多倍,有时只是快一点。

I peronsonally thing "as" is also more readable.我个人认为“作为”也更具可读性。

So, since it is both faster and "safer" (wont throw exception), and possibly easier to read, I recommend using "as" all the time.因此,由于它既更快又“更安全”(不会抛出异常),并且可能更易于阅读,因此我建议始终使用“as”。

2 is useful for casting to a derived type. 2 对于转换为派生类型很有用。

Suppose a is an Animal:假设a是一个动物:

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

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

will get a fed with a minimum of casts.用最少的铸件会得到反馈。

"(string)o" will result in an InvalidCastException as there's no direct cast. "(string)o" 将导致 InvalidCastException,因为没有直接转换。

"o as string" will result in s being a null reference, rather than an exception being thrown. “o as string”将导致 s 为空引用,而不是抛出异常。

"o.ToString()" isn't a cast of any sort per-se, it's a method that's implemented by object, and thus in one way or another, by every class in .net that "does something" with the instance of the class it's called on and returns a string. "o.ToString()" 本身并不是任何类型的强制转换,它是一种由对象实现的方法,因此以一种或另一种方式,由 .net 中的每个类通过实例“做某事”它被调用的类并返回一个字符串。

Don't forget that for converting to string, there's also Convert.ToString(someType instanceOfThatType) where someType is one of a set of types, essentially the frameworks base types.不要忘记为了转换为字符串,还有 Convert.ToString(someType instanceOfThatType) ,其中 someType 是一组类型之一,本质上是框架的基本类型。

It seems the two of them are conceptually different.看起来他们两个在概念上是不同的。

Direct Casting直接铸造

Types don't have to be strictly related.类型不必严格相关。 It comes in all types of flavors.它有各种口味。

  • Custom implicit/explicit casting: Usually a new object is created.自定义隐式/显式转换:通常会创建一个新对象。
  • Value Type Implicit: Copy without losing information.值类型隐式:复制而不丢失信息。
  • Value Type Explicit: Copy and information might be lost.值类型显式:副本和信息可能会丢失。
  • IS-A relationship: Change reference type, otherwise throws exception. IS-A 关系:改变引用类型,否则抛出异常。
  • Same type: 'Casting is redundant'.相同类型: '铸造是多余的'。

It feels like the object is going to be converted into something else.感觉这个对象要被转换成别的东西。

AS operator AS运营商

Types have a direct relationship.类型有直接关系。 As in:如:

  • Reference Types: IS-A relationship Objects are always the same, just the reference changes.引用类型: IS-A 关系对象总是相同的,只是引用发生了变化。
  • Value Types: Copy boxing and nullable types.值类型:复制装箱和可为空类型。

It feels like the you are going to handle the object in a different way.感觉就像您将以不同的方式处理对象。

Samples and IL样品和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 = o as string; // 2

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

All given answers are good, if i might add something: To directly use string's methods and properties (eg ToLower) you can't write:如果我可以添加一些东西,所有给出的答案都很好:要直接使用字符串的方法和属性(例如 ToLower),您不能写:

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

you can only write:你只能写:

((string)o).ToLower();

but you could write instead:但你可以写:

(o as string).ToLower();

The as option is more readable (at least to my opinion). as选项更具可读性(至少在我看来)。

I would like to attract attention to the following specifics of the as operator:我想引起注意as运算符的以下细节:

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

Note that the as operator performs only reference conversions, nullable conversions, and boxing conversions.请注意, as 运算符仅执行引用转换、可为空转换和装箱转换。 The as operator can't perform other conversions, such as user-defined conversions, which should instead be performed by using cast expressions. as 运算符不能执行其他转换,例如用户定义的转换,而应该使用强制转换表达式来执行这些转换。

Use direct cast string s = (string) o;使用直接转换string s = (string) o; if in the logical context of your app string is the only valid type.如果在您的应用程序string的逻辑上下文中是唯一有效的类型。 With this approach, you will get InvalidCastException and implement the principle of Fail-fast .使用这种方法,您将获得InvalidCastException并实现Fail-fast原则。 Your logic will be protected from passing the invalid type further or get NullReferenceException if used as operator.你的逻辑将受到保护,免受进一步传递了无效的类型,或者使用得到的NullReferenceException as运营商。

If the logic expects several different types cast string s = o as string;如果逻辑需要几种不同的类型将string s = o as string; and check it on null or use is operator.并检查它是否为null或使用is运算符。

New cool feature have appeared in C# 7.0 to simplify cast and check is a Pattern matching : 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;
 }

When trying to get the string representation of anything (of any type) that could potentially be null, I prefer the below line of code.当试图获取可能为空的任何(任何类型)的字符串表示时,我更喜欢下面的代码行。 It's compact, it invokes ToString(), and it correctly handles nulls.它很紧凑,它调用 ToString(),并且可以正确处理空值。 If o is null, s will contain String.Empty.如果 o 为 null,则 s 将包含 String.Empty。

String s = String.Concat(o);

既然没有人提到它,那么通过关键字最接近 Java 的 instanceOf 是这样的:

obj.GetType().IsInstanceOfType(otherObj)

The following two forms of type conversion (casting) is supported in C#: C#支持以下两种类型的类型转换(广播)形式:

| |

(C) v (简历

• Convert the static type of v to c in the given expression •在给定表达式中将v的静态类型转换为c

• Only possible if the dynamic type of v is c, or a subtype of c •仅当v的动态类型为c或c的子类型时才可能

• If not, an InvalidCastException is thrown •如果不是,则抛出InvalidCastException

| |

v as C v作为C

• Non-fatal variant of (c) v •(c)v的非致命变体

• Thus, convert the static type of v to c in the given expression •因此,在给定表达式中将v的静态类型转换为c

• Returns null if the dynamic type of v is not c, or a subtype of c •如果v的动态类型不是c或c的子类型,则返回null

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM