ChatGPT解决这个技术问题 Extra ChatGPT

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?

Not quite a duplicate, but there are also some performance discussions in a previous question.
4th: string s = Convert.ToString(o); 5th: string s = $"{o}" (or equivalently the string.Format form for earlier C#)
Since many can use this post as a reference, we can also use IS operator for casting starting with C# 7. Reference

J
John Weisz
string s = (string)o; // 1

Throws InvalidCastException if o is not a string. Otherwise, assigns o to s, even if o is null.

string s = o as string; // 2

Assigns null to s if o is not a string or if o is null. For this reason, you cannot use it with value types (the operator could never return null in that case). Otherwise, assigns o to s.

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

Causes a NullReferenceException if o is null. Assigns whatever o.ToString() returns to s, no matter what type o is.

Use 1 for most conversions - it's simple and straightforward. I tend to almost never use 2 since if something is not the right type, I usually expect an exception to occur. I have only seen a need for this return-null type of functionality with badly designed libraries which use error codes (e.g. return null = error, instead of using exceptions).

3 is not a cast and is just a method invocation. Use it for when you need the string representation of a non-string object.


RE: Anheledir Actually i would be null after the first call. You have to use an explicit conversion function to get the value of a string.
RE: Sander Actually there is another very good reason to use as, it simplifies your checking code (Check for null rather then check for null and correct type) This is helpful since a lot of the time you would rather throw a custom one exception. But it is very true that blind as calls are bad.
#2 is handy for things like Equals methods where you don't know the input type.Generally though, yes, 1 would be preferred. Although preferred over that would obviously be using the type system to restrict to one type when you only expect one :)
#2 is also useful when you have code that might do something specific for a specialised type but otherwise would do nothing.
As a general rule of thumb. Usage of as must be followed by a test to see if it is null. Or at least other code that accepts null as a valid input. That said, I use as casting quite frequently, an example where I'd use it is for a method that accepts IEnumerable<T> but can preallocate a dynamically sized collection if it's also an ICollection. ICollection col = input as ICollection; if (col != null) something.Reserve(col.Count);
w
whytheq

string s = (string)o; Use when something should definitely be the other thing. string s = o as string; Use when something might be the other thing. string s = o.ToString(); Use when you don't care what it is but you just want to use the available string representation.


I get a sense this answer sounds good, but it might not be accurate.
I like the first two, but I would add "and you are sure it isn't null" to the third option.
you can use Elvis (?.) these days to avoid having to care about that: obj?.ToString()
@Quibblesome nice answer: will you get annoyed if I add in what 1/2/3 are so that it is not necessary to scroll up to OP. I with SO would rank old answers according to votes!
B
Blair Conrad

It really depends on whether you know if o is a string and what you want to do with it. If your comment means that o really really is a string, I'd prefer the straight (string)o cast - it's unlikely to fail.

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.

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:

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. 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.

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.


One note - you can use as with nullable value types. I.E. o as DateTime won't work, but o as DateTime? will...
Why not using if (s is string) instead?
@BornToCode, for me, largely personal preference. Depending on what you're doing, often after ising, you'll have to cast again anyhow, so you have the is and then a hard cast. For some reason, the as and null check felt better to me.
Nowadays we can use: if (o is string s) { DoSomethingWithString(s); } else { ... }
M
Mark Cidade

If you already know what type it can cast to, use a C-style cast:

var o = (string) iKnowThisIsAString; 

Note that only with a C-style cast can you perform explicit type coercion.

If you don't know whether it's the desired type and you're going to use it if it is, use as keyword:

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. It will only be non-null if the object is not null and natively of the specified type.

Use ToString() to get a human-readable string representation of any object, even if it can't cast to string.


That's an interesting little gotcha regarding the type conversion operators. I have a few types that I've created conversions for, must watch out for that then.
S
Sergio Acosta

'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.

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.

Just to add an important fact:

The 'as' keyword only works with reference types. You cannot do:

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

In those cases you have to use casting.


G
Glenn Slaven

The as keyword is good in asp.net when you use the FindControl method.

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


B
Brady Moritz

According to experiments run on this page: 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. 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.


J
Joel in Gö

2 is useful for casting to a derived type.

Suppose a is an Animal:

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.


@Chirs Moutray, that is not always possible, especially if it is a library.
R
Rob

"(string)o" will result in an InvalidCastException as there's no direct cast.

"o as string" will result in s being a null reference, rather than an exception being thrown.

"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.

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.


L
Lucas Teixeira

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.

Same type: 'Casting is redundant'.

It feels like the object is going to be converted into something else.

AS operator

Types have a direct relationship. As in:

Reference Types: IS-A relationship Objects are always the same, just the reference changes.

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

    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
    }

B
BornToCode

All given answers are good, if i might add something: To directly use string's methods and properties (e.g. ToLower) you can't write:

(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).


the (o as string).ToLower() construct defeats the purpose of the as operator. This will throw a null reference exception when o cannot be cast to string.
@james - But who said that the sole purpose of the as operator is to throw exception if cast fails? If you know that o is a string and just want to write cleaner code you could use (o as string).ToLower() instead of the multiple confusing brackets.
the purpose of the as is quite the opposite - it should not throw the exception when the cast fails, it should return null. Let's say your o is a string with a value of null, what will happen then? Hint - your ToLower call will fail.
@james - You're right, but what about the cases where I know for certain that it won't be null and I just need to do the casting for the compiler to let me access that object's methods?
you can definitely do that but it's not exactly best practice because you don't want to rely on the caller or external systems to ensure your value isn't null. If you're using C#6 then you could do (o as string)?. ToLower().
M
Matt
string s = o as string; // 2

Is prefered, as it avoids the performance penalty of double casting.


Hi Chris, the link that was in this answer is now a 404... I'm not sure if you've got a replacement you want to put in in it's place?
V
V. S.

I would like to attract attention to the following specifics of the as operator:

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. The as operator can't perform other conversions, such as user-defined conversions, which should instead be performed by using cast expressions.


D
Dmitry

Use direct cast string s = (string) o; if in the logical context of your app string is the only valid type. With this approach, you will get InvalidCastException and implement the principle of Fail-fast. Your logic will be protected from passing the invalid type further or get NullReferenceException if used as operator.

If the logic expects several different types cast string s = o as string; and check it on null or use is operator.

New cool feature have appeared in C# 7.0 to simplify cast and check is a Pattern matching:

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

x
xtrem

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. If o is null, s will contain String.Empty.

String s = String.Concat(o);

B
Bennett Yeo

Since nobody mentioned it, the closest to instanceOf to Java by keyword is this:

obj.GetType().IsInstanceOfType(otherObj)