Why should casting be avoided?

24,064

Solution 1

You've tagged this with three languages, and the answers are really quite different between the three. Discussion of C++ more or less implies discussion of C casts as well, and that gives (more or less) a fourth answer.

Since it's the one you didn't mention explicitly, I'll start with C. C casts have a number of problems. One is that they can do any of a number of different things. In some cases, the cast does nothing more than tell the compiler (in essence): "shut up, I know what I'm doing" -- i.e., it ensures that even when you do a conversion that could cause problems, the compiler won't warn you about those potential problems. Just for example, char a=(char)123456;. The exact result of this implementation defined (depends on the size and signedness of char), and except in rather strange situations, probably isn't useful. C casts also vary in whether they're something that happens only at compile time (i.e., you're just telling the compiler how to interpret/treat some data) or something that happens at run time (e.g., an actual conversion from double to long).

C++ attempts to deal with that to at least some extent by adding a number of "new" cast operators, each of which is restricted to only a subset of the capabilities of a C cast. This makes it more difficult to (for example) accidentally do a conversion you really didn't intend -- if you only intend to cast away constness on an object, you can use const_cast, and be sure that the only thing it can affect is whether an object is const, volatile, or not. Conversely, a static_cast is not allowed to affect whether an object is const or volatile. In short, you have most of the same types of capabilities, but they're categorized so one cast can generally only do one kind of conversion, where a single C-style cast can do two or three conversions in one operation. The primary exception is that you can use a dynamic_cast in place of a static_cast in at least some cases and despite being written as a dynamic_cast, it'll really end up as a static_cast. For example, you can use dynamic_cast to traverse up or down a class hierarchy -- but a cast "up" the hierarchy is always safe, so it can be done statically, while a cast "down" the hierarchy isn't necessarily safe so it's done dynamically.

Java and C# are much more similar to each other. In particular, with both of them casting is (virtually?) always a run-time operation. In terms of the C++ cast operators, it's usually closest to a dynamic_cast in terms of what's really done -- i.e., when you attempt to cast an object to some target type, the compiler inserts a run-time check to see whether that conversion is allowed, and throw an exception if it's not. The exact details (e.g., the name used for the "bad cast" exception) varies, but the basic principle remains mostly similar (though, if memory serves, Java does make casts applied to the few non-object types like int much closer to C casts -- but these types are used rarely enough that 1) I don't remember that for sure, and 2) even if it's true, it doesn't matter much anyway).

Looking at things more generally, the situation's pretty simple (at least IMO): a cast (obviously enough) means you're converting something from one type to another. When/if you do that, it raises the question "Why?" If you really want something to be a particular type, why didn't you define it to be that type to start with? That's not to say there's never a reason to do such a conversion, but anytime it happens, it should prompt the question of whether you could re-design the code so the correct type was used throughout. Even seemingly innocuous conversions (e.g., between integer and floating point) should be examined much more closely than is common. Despite their seeming similarity, integers should really be used for "counted" types of things and floating point for "measured" kinds of things. Ignoring the distinction is what leads to some of the crazy statements like "the average American family has 1.8 children." Even though we can all see how that happens, the fact is that no family has 1.8 children. They might have 1 or they might 2 or they might have more than that -- but never 1.8.

Solution 2

Lots of good answers here. Here's the way I look at it (from a C# perspective).

Casting usually means one of two things:

  • I know the runtime type of this expression but the compiler does not know it. Compiler, I am telling you, at runtime the object that corresponds to this expression is really going to be of this type. As of now, you know that this expression is to be treated as being of this type. Generate code that assumes that the object will be of the given type, or, throw an exception if I'm wrong.

  • Both the compiler and the developer know the runtime type of the expression. There is another value of a different type associated with the value that this expression will have at runtime. Generate code that produces the value of the desired type from the value of the given type; if you cannot do so, then throw an exception.

Notice that those are opposites. There are two kinds of casts! There are casts where you are giving a hint to the compiler about reality - hey, this thing of type object is actually of type Customer - and there are casts where you are telling the compiler to perform a mapping from one type to another - hey, I need the int that corresponds to this double.

Both kinds of casts are red flags. The first kind of cast raises the question "why exactly is it that the developer knows something that the compiler doesn't?" If you are in that situation then the better thing to do is usually to change the program so that the compiler does have a handle on reality. Then you don't need the cast; the analysis is done at compile time.

The second kind of cast raises the question "why isn't the operation being done in the target data type in the first place?" If you need a result in ints then why are you holding a double in the first place? Shouldn't you be holding an int?

Some additional thoughts here:

Link

Solution 3

Casting errors are always reported as run-time errors in java. Using generics or templating turns these errors into compile-time errors, making it much easier to detect when you have made a mistake.

As I said above. This isn't to say that all casting is bad. But if it is possible to avoid it, its best to do so.

Solution 4

Casting is not inherently bad, it's just that it's often misused as a means to achieve something that really should either not be done at all, or done more elegantly.

If it was universally bad, languages would not support it. Like any other language feature, it has its place.

My advice would be to focus on your primary language, and understand all its casts, and associated best practices. That should inform excursions into other languages.

The relevant C# docs are here.

There is a great summary on C++ options at a previous SO question here.

Solution 5

I'm mostly speaking for C++ here, but most of this probably applies to Java and C# as well:

C++ is a statically typed language. There are some leeways the language allows you in this (virtual functions, implicit conversions), but basically the compiler knows the type of every object at compile-time. The reason to use such a language is that errors can be caught at compile-time. If the compiler know the types of a and b, then it will catch you at compile-time when you do a=b where a is a complex number and b is a string.

Whenever you do explicit casting you tell the compiler to shut up, because you think you know better. In case you're wrong, you will usually only find out at run-time. And the problem with finding out at run-time is, that this might be at a customer's.

Share:
24,064

Related videos on Youtube

LoudNPossiblyWrong
Author by

LoudNPossiblyWrong

yet another coder.

Updated on February 11, 2022

Comments

  • LoudNPossiblyWrong
    LoudNPossiblyWrong about 2 years

    I generally avoid casting types as much as possible since I am under the impression that it's poor coding practice and may incur a performance penalty.

    But if someone asked me to explain why exactly that is, i would probably look at them like a deer in headlights.

    So why/when is casting bad?

    Is it general for java, c#, c++ or does every different runtime environment deal with it on it's own terms?

    Specifics for a any language are welcome, example why is it bad in c++?

    • Oded
      Oded over 13 years
      Where did you get this impression?
    • LoudNPossiblyWrong
      LoudNPossiblyWrong over 13 years
      To be more specific, what is happening in the runtime mechanism that makes it so bad, how does casting work at the machine level?
    • LoudNPossiblyWrong
      LoudNPossiblyWrong over 13 years
      I got this impression since i'v never read a book or met a programmer that said "CASTING SOOOO GOOOOD!!!"
    • James McNellis
      James McNellis over 13 years
      The answer for C++ is inevitably different from the answer for C#. This is very language-specific. The answers thus far answer this question for specific languages and in some cases do not state what language they are talking about.
    • Mike Miller
      Mike Miller over 13 years
      Bad is a relative term. Avoiding casting is a best-practice, but sometimes a programmer has gotta do what a programmer has got to do. (especially if you writing a java 1.5+ program that uses a library written for 1.4) Perhaps rename the question, "Why should casting be avoided?"
    • Pete Kirkham
      Pete Kirkham over 13 years
      "what is happening in the runtime mechanism that makes it so bad, " usually it's bad because it's removing any design constraints the compiler is checking for you, not because of the any runtime cost. Whenever I do a contract at a MFC shop, the code's full of reinterpret_cast<CSomeThingOrOther> ( wparam ) which makes it impossible to refactor the code without 100% test coverage, and getting 100% coverage of an MFC application is nigh on impossible.
    • cthulhu
      cthulhu over 13 years
      "So why/when is casting bad?" - better question would be "When is it good?", imho. I haven't had to use it at all for quite some time, only when working with legacy (pre-Generics Java) code.
    • reach4thelasers
      reach4thelasers over 7 years
      This is a great question.... It was closed by 5 users who each have less than 10k reputation!! An overzealous use of new powers, clearly. Have voted to re-open.
    • gnat
      gnat over 7 years
      @Brian this question is a poor fit for Programmers - it would be quickly voted down and closed over there, see meta.programmers.stackexchange.com/questions/6483/… Recommended reading: What goes on Programmers.SE? A guide for Stack Overflow
    • Brian
      Brian over 7 years
      @gnat I would argue that this falls into the first example of an on-topic question for Programmers, "software development methods and practices", and none of the off-topic categories.
    • Thomas Owens
      Thomas Owens over 7 years
      @Brian Did you read the Wikipedia article linked to for what is meant by "methods and practices"? It doesn't mean this. It means things process like Scrum, Extreme Programming, CMMI, ISO9001 and methods like TDD, BDD, and CI/CD. It may be on-topic as a software design question, but like here, it would be closed as too broad. We have the same rules for things like primarily opinion based, too broad, and unclear as Stack Overflow does.
    • Brian
      Brian over 7 years
      @ThomasOwens Ah, okay, that makes sense.
  • James McNellis
    James McNellis over 13 years
    Not all casts "bypass" type safety in C++.
  • Admin
    Admin over 13 years
    This assumes that all casting is "bad"; however, this is not entirely true. Take C#, for instance, with both implicit and explicit cast support. The problem comes when a cast is performed which (accidentally) removes information or type-safety (this differs by language).
  • Admin
    Admin over 13 years
    Not all casts "bypass" type safety in C#.
  • Steve Townsend
    Steve Townsend over 13 years
    Looks like you are suffering 'death by attention span' here, this is a nice answer imo.
  • Erick Robertson
    Erick Robertson over 13 years
    Not all casts "bypass" type safety in Java.
  • Steve Townsend
    Steve Townsend over 13 years
    Actually, this is flat out wrong in C++. Perhaps an edit to include the language(s) targeted with this information is in order.
  • Steve Townsend
    Steve Townsend over 13 years
    @Erick - I would, but I don't know the first thing about Java, nor enough C# detail to be sure the info is correct.
  • sbi
    sbi over 13 years
    Not all casts "bypass" type safety in Casting. Oh wait, that tag doesn't refer to a language...
  • Travis Gockel
    Travis Gockel over 13 years
    In almost all cases in C# and Java, casting will give you a performance degradation, since the system will perform a run-time type check (which is not free). In C++, dynamic_cast is generally slower than static_cast, since it generally has to do run-time type checking (with some caveats: casting to a base type is cheap, etc).
  • Mike Miller
    Mike Miller over 13 years
    Sorry, I'm a Java guy, so my c++ knowledge is limited enough. I could edit the word "templating" out of it, as it was intended to be vague.
  • M2tM
    M2tM over 13 years
    A strong answer, but a few of the "I don't know" portions could be tightened up to make it comprehensive. @Dragontamer5788 it is a good answer, but not comprehensive.
  • Mike Caron
    Mike Caron over 13 years
    I don't think those are inherently red flags. If you have a Foo object that inherits from Bar, and you store that in a List<Bar>, then you're going to need casts if you want that Foo back. Perhaps it indicates a problem at an architectural level (why are we storing Bars instead of Foos?), but not necessarily. And, if that Foo also has a valid cast to int, it also deals with your other comment: You're storing a Foo, not an int, because an int is not always appropriate.
  • Jeffrey L Whitledge
    Jeffrey L Whitledge over 13 years
    @Mike Caron - I can't answer for Eric, obviously, but to me a red flag means "this is something to think about", not "this is something wrong". And there's no trouble storing a Foo in a List<Bar>, but at the point of the cast you are trying to do something with Foo that is not appropriate for a Bar. That means that differing behavior for subtypes is being done through a mechanism other than the built-in polymorphism provided by virtual methods. Maybe that's the right solution, but more often it's a red flag.
  • Eric Lippert
    Eric Lippert over 13 years
    Indeed. If you're pulling stuff out of a list of animals and you later need to tell the compiler, oh, by the way, I happen to know that the first one is a tiger, the second one is a lion, and the third one is a bear, then you should have been using a Tuple<Lion, Tiger, Bear>, not a List<Animal>.
  • Pete Kirkham
    Pete Kirkham over 13 years
    Many definitions of 'strongly typed' are based on 'if it complies without type errors, then it cannot have a type failure at runtime'. As such, strongly typed languages exclude casting which can cause exceptions, such as Java and C# casts, and C++ dynamic_cast to a reference or reinterpret_cast. It's also hard to have null in a strongly typed language, as that blows away most type safety - if you invoke a method on a null receiver, it can fail as the null type is a subtype of all types. Perhaps you meant mostly statically typed, with a bit of dynamic typing on the side.
  • Oz.
    Oz. over 13 years
    One whole child and one child with a missing leg would be 1.8 children. But very nice answer none the less. :)
  • Admin
    Admin over 13 years
    A very nice answer not hampered in the least by excluding couples with no children.
  • Jerry Coffin
    Jerry Coffin over 13 years
    @Roger: not excluding, just ignoring.
  • user207421
    user207421 over 13 years
    Not so. Some cast errors are caught by the Java compiler, and not just generic ones either. Consider (String)0;
  • Kdeveloper
    Kdeveloper over 13 years
    @James McNellis, @pst, @Erick Robertson, @sbi, @Travis Gockel: Good points, I included it in the answer.
  • kvb
    kvb over 13 years
    I agree with you, but I think that without better syntactic support for Tuple<X,Y,...> tuples are unlikely to see widespread use in C#. This is one place where the language could do a better job of pushing people towards the "pit of success".
  • Eric Lippert
    Eric Lippert over 13 years
    @kvb: I agree. We considered adopting a tuple syntax for C# 4 but it did not fit into the budget. Perhaps in C# 5; we haven't worked out the full feature set yet. Too busy getting the CTP together for async. Or perhaps in a hypothetical future version.
  • user207421
    user207421 over 13 years
    I didn't say they would. I was correcting your statement above that casting error are always reported as runtime errors in Java. And it's not confined to constants. Any cast between classes that don't share inheritance or an interface is detected at compile time. The statement is incorrect.
  • Bill
    Bill over 11 years
    @Eric: Does your tuple example cover the case of a heterogeneous coontainers of Animals, as mentioned by user168715 below? For example, I may know my List<Animal> contains one or more Lions and Tigers and Bears, but I do not know or care about the quantity or sequence of list items. If I want to be able to iterate through the List and check / cast to the specialized (derived) type to access its special behaviors, but yet, want to store them in a single container, then this would seem an appropriate use of casting.
  • Eric Lippert
    Eric Lippert over 11 years
    @Bill: Then instead of casting I would use the OfType<T> extension method. foreach(Tiger t in animals.OfType<Tiger>()) is very clear. Or I would make the base class have a virtual method that did the special behaviour; then you don't need to do a cast followed by an instance dispatch; you just do a virtual dispatch.
  • Bill
    Bill over 11 years
    @Eric: I am actually thinking of a case where the derived class has a single Property that returns a different type: eg, LionInfo Info {get;set;} TigerInfo Info {get;set;} BearInfo... Peter Lawry suggest an interesting solution (stackoverflow.com/a/5617146/243272) using a generic Holder class that would normalize the return type of the Info property in the main class (ie Animal) to Holder. This provides some encapsulation without a proliferation of slightly different types; eventually, however, you need to ask what is in Holder anyway, presumably via a type check / cast. Thoughts?
  • Bill
    Bill over 11 years
    @Eric: Just to round out my last comment, the idea would be that I would want to branch to different handling code depending on what was in Holder. This seems like a sound approach with all the benefits of a type safe approach, and the best you can do without resorting to using a dynamic variable.
  • Tyler Pantuso
    Tyler Pantuso over 8 years
    In C#, I find myself casting all the time in event handlers that are used for more than one control: switch ((sender as Button).Name) { case "btnSubmit": ... }.