By nullDev


2008-09-25 10:09:18 8 Comments

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?

17 comments

@Vadim S. 2018-10-17 12:11:02

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.

@user4931677 2018-05-27 13:17:48

The following two forms of type conversion (casting) is supported in C#:

|

(C) v

• Convert the static type of v to c in the given expression

• Only possible if the dynamic type of v is c, or a subtype of c

• If not, an InvalidCastException is thrown

|

v as C

• Non-fatal variant of (c) v

• Thus, convert the static type of v to c in the given expression

• Returns null if the dynamic type of v is not c, or a subtype of c

@Dmitry 2017-08-03 07:34:30

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

@Lucas Teixeira 2016-09-25 04:47:48

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
    }

@Sander 2008-09-25 10:16:26

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.

@Anheledir 2008-09-25 10:41:25

You can assign 'null' to value-types when explicitly defined, e.g.: int? i; string s = "5"; i = s as int; // i is now 5 s = null; i = s as int; // i is now null

@Guvante 2008-09-25 10:47:18

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.

@Guvante 2008-09-25 10:48:50

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.

@Calum 2008-09-25 11:22:45

#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 :)

@AnthonyWJones 2008-09-25 11:45:15

#2 is also useful when you have code that might do something specific for a specialised type but otherwise would do nothing.

@nXqd 2011-10-08 13:35:04

Thanks so much for this . I thought #2 is the modern style of casting :) which is quite stupid ...

@Alex KeySmith 2011-10-25 11:08:48

Hi @Sander, on 2. Could it be used for value types like this int? test = i as int?;

@Dave Cousineau 2012-06-20 01:52:07

you can also do the following: int val = something as int? ?? someDefaultInt;

@Learner 2013-12-01 11:01:03

Sander: Your 2nd point didn't make any sense to me. Atleast I didn't understand. Also, you say you always expect exception to occur, that is not a good practise either.

@Aidiakapi 2015-04-28 16:31:49

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

@Suamere 2015-11-04 15:59:54

Thus far, I have only used 2 as for overriding Equals: public override bool Equals(object obj) { if ((obj as MyObject) == null) return base.Equals(obj); /*return: Handle it myself*/ } because that override uses object as a parameter and you have zero control about what people will pass into there.

@Eniola 2016-06-20 07:08:35

Why do you say returning error codes rather than throwing exceptions is bad design? Isnt exception throwing and handling, especially on tight loops, more expensive? What if the given library chooses to work around that limitation with the primitive approach of using error codes? Your statement will imply that the Win32 API is badly designed?

@Bennett Yeo 2015-10-13 20:21:25

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

obj.GetType().IsInstanceOfType(otherObj)

@Chris S 2008-09-25 11:05:43

string s = o as string; // 2

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

@Matt 2015-06-25 22:23:22

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?

@BornToCode 2014-04-03 14:28:13

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

@james 2016-08-31 07:43:03

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.

@BornToCode 2016-08-31 11:39:26

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

@james 2016-09-01 20:40:07

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.

@BornToCode 2016-09-03 21:11:55

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

@james 2016-09-04 23:49:28

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

@Quibblesome 2008-09-25 10:31:28

  1. Use when something should definitely be the other thing.
  2. Use when something might be the other thing.
  3. Use when you don't care what it is but you just want to use the available string representation.

@kdbanman 2015-07-31 17:51:40

This is an excellent addition to the accepted answer.

@j riv 2017-07-02 09:20:49

I get a sense this answer sounds good, but it might not be accurate.

@Quibblesome 2017-07-04 13:11:29

@jriv its worked for me for the past 15 years and still works so.....

@Uxonith 2017-09-13 22:42:24

I like the first two, but I would add "and you are sure it isn't null" to the third option.

@Quibblesome 2017-09-14 11:39:40

you can use Elvis (?.) these days to avoid having to care about that: obj?.ToString()

@Griswald_911 2018-08-13 20:08:54

@Quibblesome -- great answer but I had to stop to think about your rebuttle! it literally blows my mind that the language has been around well over 15 years. It feels like yesterday when we were being all "edgy" trying to convince senior devs to make the switch to C#.

@Quibblesome 2018-08-14 08:59:04

rebuttal* for the record.

@xtrem 2012-06-23 01:31:48

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

@Rob 2008-09-25 10:17:50

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

@Brady Moritz 2010-08-15 18:36:54

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.

@Glenn Slaven 2008-09-25 11:17:27

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

@Mark Cidade 2008-09-25 10:41:43

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.

@AnthonyWJones 2008-09-25 11:48:12

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.

@Joel in Gö 2008-09-25 10:31:39

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.

@Chris Moutray 2012-05-09 09:38:19

But it would be better to use polymorphism for this type of problem...

@qreba47jhqb4e3lstrujvvdx 2012-11-24 03:53:57

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

@Blair Conrad 2008-09-25 10:16:50

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.

@John Gibb 2014-01-13 18:46:05

One note - you can use as with nullable value types. I.E. o as DateTime won't work, but o as DateTime? will...

@BornToCode 2014-04-03 14:17:31

Why not using if (s is string) instead?

@Blair Conrad 2014-04-03 14:29:08

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

@Sergio Acosta 2008-09-25 10:15:32

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

@leppie 2008-09-25 10:18:28

'is' works on any type

@Sergio Acosta 2008-09-25 10:22:03

Thanks for pointing my mistake, you are right. I edited the answer. oops, sorry.

Related Questions

Sponsored Content

11 Answered Questions

296 Answered Questions

[SOLVED] Hidden Features of C#?

  • 2008-08-12 16:32:24
  • Serhat Ozgel
  • 654435 View
  • 1476 Score
  • 296 Answer
  • Tags:   c# hidden-features

10 Answered Questions

[SOLVED] When to use reinterpret_cast?

  • 2009-02-21 16:12:39
  • HeretoLearn
  • 293204 View
  • 396 Score
  • 10 Answer
  • Tags:   c++ casting

24 Answered Questions

[SOLVED] Cast int to enum in C#

  • 2008-08-27 03:58:21
  • lomaxx
  • 1154442 View
  • 2819 Score
  • 24 Answer
  • Tags:   c# enums casting

26 Answered Questions

[SOLVED] Do I cast the result of malloc?

  • 2009-03-03 10:13:02
  • Patrick McDonald
  • 196790 View
  • 2221 Score
  • 26 Answer
  • Tags:   c malloc casting

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

23 Answered Questions

[SOLVED] What's the difference between the 'ref' and 'out' keywords?

10 Answered Questions

[SOLVED] Should 'using' directives be inside or outside the namespace?

18 Answered Questions

[SOLVED] Casting vs using the 'as' keyword in the CLR

  • 2009-01-30 16:20:52
  • Frank V
  • 68276 View
  • 364 Score
  • 18 Answer
  • Tags:   c# casting clr

8 Answered Questions

[SOLVED] Regular cast vs. static_cast vs. dynamic_cast

  • 2008-08-26 13:20:55
  • Graeme Perrow
  • 632912 View
  • 1589 Score
  • 8 Answer
  • Tags:   c++ pointers casting

Sponsored Content