777
votes

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?

16
Not quite a duplicate, but there are also some performance discussions in a previous question.Unsliced
4th: string s = Convert.ToString(o); 5th: string s = $"{o}" (or equivalently the string.Format form for earlier C#)Earth Engine

16 Answers

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

376
votes
  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.
31
votes

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.

9
votes

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.

7
votes

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

6
votes

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

6
votes

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.

5
votes

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.

4
votes

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

4
votes

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
    }
3
votes

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

3
votes
string s = o as string; // 2

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

2
votes

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.

0
votes

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);
0
votes

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

obj.GetType().IsInstanceOfType(otherObj)
0
votes

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