Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
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++?
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.
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
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.
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.
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.
Java, c# and c++ are strongly typed languages, although strongly typed languages can be seen as inflexible, they have the benefit of doing type checking at compile time and protect you against runtime errors that are caused by having the wrong type for certain operations.
There are basicaly two kind of casts: a cast to a more general type or a cast to an other types (more specific). Casting to a more general type (casting to a parent type) will leave the compile time checks intact. But casting to other types (more specific types) will disable compile time type checking and will be replaced by the compiler by a runtime check. This means you have less certainty you’re compiled code will run correctly. It also has some negligible performance impact, due to the extra runtime type check (the Java API is full of casts!).
Some types of casting are so safe and efficient as to often not even be considered casting at all.
If you cast from a derived type to a base type, this is generally quite cheap (often - depending on language, implementation and other factors - it is zero-cost) and is safe.
If you cast from a simple type like an int to a wider type like a long int, then again it is often quite cheap (generally not much more expensive than assigning the same type as that cast to) and again is safe.
Other types are more fraught and/or more expensive. In most languages casting from a base type to a derived type is either cheap but has a high risk of severe error (in C++ if you static_cast from base to derived it will be cheap, but if the underlying value is not of the derived type the behaviour is undefined and can be very strange) or relatively expensive and with a risk of raising an exception (dynamic_cast in C++, explicit base-to-derived cast in C#, and so on). Boxing in Java and C# is another example of this, and an even greater expense (considering that they are changing more than just how the underlying values are treated).
Other types of cast can lose information (a long integer type to a short integer type).
These cases of risk (whether of exception or a more serious error) and of expense are all reasons to avoid casting.
A more conceptual, but perhaps more important, reason is that each case of casting is a case where your ability to reason about the correctness of your code is stymied: Each case is another place where something can go wrong, and the ways in which it can go wrong add to the complexity of deducing whether the system as a whole will go wrong. Even if the cast is provably safe each time, proving this is an extra part of the reasoning.
Finally, the heavy use of casts can indicate a failure to consider the object model well either in creating it, using it, or both: Casting back and forth between the same few types frequently is almost always a failure to consider the relationships between the types used. Here it's not so much that casts are bad, as they are a sign of something bad.
There is a growing tendency for programmers to cling to dogmatic rules about use of language features ("never use XXX!", "XXX considered harmful", etc), where XXX ranges from gotos to pointers to protected data members to singletons to passing objects by value.
Following such rules, in my experience, ensures two things: you will not be a terrible programmer, nor will you be a great programmer.
A much better approach is to dig down and uncover the kernel of truth behind these blanket prohibitions, and then use the features judiciously, with the understanding that there are many situations for which they're the best tool for the job.
"I generally avoid casting types as much as possible" is a good example of such an overgeneralized rule. Casts are essential in many common situations. Some examples:
When interoperating with third-party code (especially when that code is rife with typedefs). (Example: GLfloat <--> double <--> Real.)
Casting from a derived to base class pointer/reference: This is so common and natural that the compiler will do it implicitly. If making it explicit increases readability, the cast is a step forwards, not backwards!
Casting from a base to derived class pointer/reference: Also common, even in well-designed code. (Example: heterogeneous containers.)
Inside binary serialization/deserialization or other low-level code that needs access to the raw bytes of built-in types.
Any time when it's just plain more natural, convenient, and readable to use a different type. (Example: std::size_type --> int.)
There are certainly many situations where it's not appropriate to use a cast, and it's important to learn these as well; I won't go into too much detail since the answers above have done a good job pointing some of them out.
To elaborate on KDeveloper's answer, it's not inherently type-safe. With casting, there is no guarantee that what you are casting from and casting to will match, and if that occurs, you will get a runtime exception, which is always a bad thing.
With specific regards to C#, because it includes the is and as operators, you have the opportunity to (for the most part) make the determination as to whether or not a cast would succeed. Because of this, you should take the appropriate steps to determine whether or not the operation would succeed and proceed appropriately.
In case of C#, one needs to be more careful while casting because of boxing/unboxing overheads involved while dealing with value types.
Not sure if someone already mentioned this, but in C# casting can be used in a rather safe manner, and is often necessary. Suppose you receive an object which can be of several types. Using the is keyword you can first confirm that the object is indeed of the type you are about to cast it to, and then cast the object to that type directly. (I didn't work with Java much but I'm sure there's a very straightforward way of doing it there as well).
You only cast an object to some type, if 2 conditions are met:
you know it is of that type
the compiler doesn't
This means not all the information you have is well represented in the type structure you use. This is bad, because your implementation should semantically comprise your model, which it clearly doesn't in this case.
Now when you do a cast, then this can have 2 different reasons:
You did a bad job in expressing the type relationships.
the languages type system simply is not expressive enough to phrase them.
In most languages you run into the 2nd situation a lot of times. Generics as in Java help a bit, the C++ template system even more, but it is hard to master and even then some things may be impossible or just not worth the effort.
So you could say, a cast is a dirty hack to circumvent your problems to express some specific type relationship in some specific language. Dirty hacks should be avoided. But you can never live without them.
Generally templates (or generics) are more type safe than casts. In that respect, i would say that an issue with casting is type-safety. However, there is another more subtle issue associated especially with downcasting: design. From my perspective at least, downcasting is a code smell, an indication that something might be wrong with my desing and i should investigate further. Why is simple: if you "get" the abstractions right, you simply don't need it! Nice question by the way...
Cheers!
To be really concise, a good reason is because of portability. Different architecture that both accommodate the same language might have, say, different sized ints. So if I migrate from ArchA to ArchB, which has a narrower int, I might see odd behavior at best, and seg faulting at worst.
(I'm clearly ignoring architecture independent bytecode and IL.)
Related
It is common to read around that object casting is a bad practice and should be avoided, for instance Why should casting be avoided? question has gotten some answers with great arguments:
By Jerry Coffin:
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.
By Eric Lippert:
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?
Moving on to my question, recently I have started to look into the source code of the well known open source project AutoFixture originally devloped by Mark Seemann which I really appreciate.
One of the main components of the library is the interface ISpecimenBuilder which define an somehow abstract method:
object Create(object request, ISpecimenContext context);
As you can see request parameter type is object and by such it accepts completely different types, different implementations of the interface treat different requests by their runtime type, checking if it is something they cable dealing with otherwise returning some kind of no response representation.
It seems that the design of the interface does not adhere to the "good practice" that object casting should be used sparsely.
I was thinking to myself if there is a better way to design this contract in a way that defeats all the casting but couldn't find any solution.
Obviously the object parameter could be replaced with some marker interface but it will not save us the casting problem, I have also thought that it is possible to use some variation of visitor pattern as described here but it does not seem to be very scalable, the visitor will must have dozens of different methods since there is so many different implementations of the interface that capable dealing with different types of requests.
Although the fact that I basically agree with the arguments against using casting as part of a good design in this specific scenario it seems as not only the best option but also the only realistic one.
To sum up, is object casting and a very general contracts are inevitability of reality when there is a need to design modular and extendable architecture?
I don't think that I can answer this question generally, for any type of application or framework, but I can offer an answer that specifically talks about AutoFixture, as well as offer some speculation about other usage scenarios.
If I had to write AutoFixture from scratch today, there's certainly things I'd do differently. Particularly, I wouldn't design the day-to-day API around something like ISpecimenBuilder. Rather, I'd design the data manipulation API around the concept of functors and monads, as outlined here.
This design is based entirely on generics, but it does require statically typed building blocks (also described in the article) known at compile time.
This is closely related to how something like QuickCheck works. When you write QuickCheck-based tests, you must supply generators for all of your own custom types. Haskell doesn't support run-time casting of values, but instead relies exclusively on generics and some compile-time automation. Granted, Haskell's generics are more powerful than C#'s, so you can't necessarily transfer the knowledge gained from Haskell to C#. It does suggest, however, that it's possible to write code entirely without relying on run-time casting.
AutoFixture does, however, support user-defined types without the need for the user to write custom generators. It does this via .NET Reflection. In .NET, the Reflection API is untyped; all the methods for generating objects and invoking members take object as input and return object as output.
Any application, library, or framework based on Reflection will have to perform some run-time casting. I don't see how to get around that.
Would it be possible to write data generators without Reflection? I haven't tried the following, but perhaps one could adopt a strategy where one would write 'the code' for a data generator directly in IL and use Reflection emit to dynamically compile an in-memory assembly that contains the generators.
This is a bit like how the Hiro container works, IIRC. I suppose that one could design other types of general-purpose frameworks around this concept, but I rarely see it done in .NET.
My questions are:
When should we use value types and when reference types?
What are the advantages and disadvantages of one over other?
What if one uses reference types everywhere? Is there any harm in it?
Please also discuss advantages and disadvantages of each one. I want to understand that as well.
You should use value types for small, immutable types which represent values.
Never make mutable structs.
For everything else, use reference types.
Use value types for immutables that do not have an identity of their own (a 1 is a 1), use reference types for other things.
There seems to be a lot of confusion over this, and Jon Skeet does a good job of clearing it up in his book "C# In Depth, 2nd Ed." (section 2.3).
My personal approach, which may or may not be right, is to ONLY use structs/enumerations (value types) to represent lightweight, atomic data structures that I know I'll be using frequently in some kind of logical or mathematical operations - think Point, etc.
That way I figure I can avoid the garbage collection performance penalty. However, Jon points out in that section of his book that there's no real guarantee, especially in new versions of the runtime, whether something will go on the stack at all.
So my best answer is use things like structs sparingly and be very conscious about why you're using them if you do. Watch out for premature optimization. And read that section in Jon's book if you can get your hands on a copy, because he does a good job of clarifying this whole topic.
Related: When to use struct?
http://www.albahari.com/valuevsreftypes.aspx
this is my reference on this point. I mainly use reference types tbh. IE classes and not structs. The main point that often gets said is that structs should only be used for small pieces of information. Really depends on the exact circumstances. Have a look at the .net framework in the object browser that should help, you will see what the microsoft guys have done and you can analyze why they made certain classes and structs.
Immutable value types and immutable reference types are semantically all but identical; the only differences are that reference types support reference equality checks that may or may not be meaningful, and that value types may be wrapped in a Nullable(Of T) while reference types are implicitly nullable. If a type is going to be immutable, depending how it will be used, there may be performance reasons to favor a struct or a class; structs are faster for some operations (nearly all operations, for sizes less than four bytes), while classes may bare faster for some others (especially for things larger than 16 bytes). Further, some types of operations are essentially impossible with structs.
Mutable struct types are useful, contrary to what some naysayers claim, but there are some caveats. If one has a variable that holds a reference to a mutable class object, and one does something to change that object, that change will effectively be "seen" by everything that holds a reference to that object. If one wishes to change an object without disturbing anything else, one must know that one holds the only reference to that object. Often times the only way to be sure of this is to copy all the data from the object into a new object instance, and then make the change to that new instance. By contrast, if one has a mutable struct, one can simply make whatever changes one wants without having to create a new instance.
The only real problem with mutable structs is that .net uses various abstractions to make them behave as part of the unified type system, and these abstractions may cause copies of structures to be used in places where the originals logically should be used. It's not always obvious when these substitutions may occur, and they can lead to confusing and erroneous behavior.
My questions are:
When should we use value types and when reference types?
What are the advantages and disadvantages of one over other?
What if one uses reference types everywhere? Is there any harm in it?
Please also discuss advantages and disadvantages of each one. I want to understand that as well.
You should use value types for small, immutable types which represent values.
Never make mutable structs.
For everything else, use reference types.
Use value types for immutables that do not have an identity of their own (a 1 is a 1), use reference types for other things.
There seems to be a lot of confusion over this, and Jon Skeet does a good job of clearing it up in his book "C# In Depth, 2nd Ed." (section 2.3).
My personal approach, which may or may not be right, is to ONLY use structs/enumerations (value types) to represent lightweight, atomic data structures that I know I'll be using frequently in some kind of logical or mathematical operations - think Point, etc.
That way I figure I can avoid the garbage collection performance penalty. However, Jon points out in that section of his book that there's no real guarantee, especially in new versions of the runtime, whether something will go on the stack at all.
So my best answer is use things like structs sparingly and be very conscious about why you're using them if you do. Watch out for premature optimization. And read that section in Jon's book if you can get your hands on a copy, because he does a good job of clarifying this whole topic.
Related: When to use struct?
http://www.albahari.com/valuevsreftypes.aspx
this is my reference on this point. I mainly use reference types tbh. IE classes and not structs. The main point that often gets said is that structs should only be used for small pieces of information. Really depends on the exact circumstances. Have a look at the .net framework in the object browser that should help, you will see what the microsoft guys have done and you can analyze why they made certain classes and structs.
Immutable value types and immutable reference types are semantically all but identical; the only differences are that reference types support reference equality checks that may or may not be meaningful, and that value types may be wrapped in a Nullable(Of T) while reference types are implicitly nullable. If a type is going to be immutable, depending how it will be used, there may be performance reasons to favor a struct or a class; structs are faster for some operations (nearly all operations, for sizes less than four bytes), while classes may bare faster for some others (especially for things larger than 16 bytes). Further, some types of operations are essentially impossible with structs.
Mutable struct types are useful, contrary to what some naysayers claim, but there are some caveats. If one has a variable that holds a reference to a mutable class object, and one does something to change that object, that change will effectively be "seen" by everything that holds a reference to that object. If one wishes to change an object without disturbing anything else, one must know that one holds the only reference to that object. Often times the only way to be sure of this is to copy all the data from the object into a new object instance, and then make the change to that new instance. By contrast, if one has a mutable struct, one can simply make whatever changes one wants without having to create a new instance.
The only real problem with mutable structs is that .net uses various abstractions to make them behave as part of the unified type system, and these abstractions may cause copies of structures to be used in places where the originals logically should be used. It's not always obvious when these substitutions may occur, and they can lead to confusing and erroneous behavior.
This question already has answers here:
Inheritance on a constrained generic type parameter
(3 answers)
Closed 9 years ago.
Why cannot C# generics derive from one of the generic type parameters like they can in C++ templates? I mean I know it impossible because CLR does not support this, but why?
I am aware of the profound differences between C++ templates and C# generics - the former are compile time entities and must be resolved during the compilation, while the latter are first class run-time entities.
Still, I am failing to see the reason why CLR designers did not come up with a scheme which would ultimately enable a CLR generic type to derive from one of its generic type parameters. After all, this would be tremendously useful feature, I personally miss it greatly.
EDIT:
I would like to know of a hard-core issue, fixing which yields such a high price on implementing this feature that justifies it not being implemented yet. For instance, examine this fictional declaration:
class C<T> : T
{
}
As Eric Lippert has noticed what if "What if T is a struct? What if T is a sealed class type? What if T is an interface type? What if T is C?! What if T is a class dervied from C? What if T is an abstract type with an abstract method? What if T has less accessibility than C ? What if T is System.ValueType? (Can you have a non-struct which inherits from System.ValueType?) What about System.Delegate, System.Enum, and so on?"
As Eric continues, "Those are the easy, obvious ones". Indeed, he is right. I am interested in a concrete example of some neither easy nor obvious issue, which is hard to resolve.
Well, start by asking yourself what could possibly go wrong with class C<T> : T { }. A huge number of things come immediately to mind:
What if T is a struct? What if T is a sealed class type? What if T is an interface type? What if T is C<T>?! What if T is a class derived from C<T>? What if T is an abstract type with an abstract method? What if T has less accessibility than C ? What if T is System.ValueType? (Can you have a non-struct which inherits from System.ValueType?) What about System.Delegate, System.Enum, and so on?
Those are the easy, obvious ones. The proposed feature opens up literally hundreds, if not thousands of more subtle questions about the interaction between the type and its base type, all of which would have to be carefully specified, implemented and tested. We'd undoubtedly miss some, and thereby cause breaking changes in the future, or saddle the runtime with implementation-defined behaviour.
The costs would be enormous, so the benefit had better be enormous. I'm not seeing an enormous benefit here.
OK, if you didn't like my previous answer, then let's take a different tack.
Your question presupposes a falsehood: that we need a reason to not implement a feature. On the contrary, we need a very, very good reason to implement any feature. Features are enormously expensive in their up-front costs, in their maintenance costs, and in the opportunity costs. (That is, the time you spend on feature X is time you cannot spend on doing feature Y, and which might prevent you from ever doing feature Z.) In order to responsibly deliver value to our customers and stakeholders, we cannot implement every feature that someone happens to like.
It's not up to the runtime designers to justify why they did not implement a feature that you find particularly nice. Features are prioritized based on their costs vs the benefit to users, and users have not exactly been hammering down my door demanding this kind of inheritance. This particular feature would massively change how analysis of the type system works in the runtime, have far-reaching effects on every language that consumes generics, and seems to me to provide very little benefit.
We use this sort of inheritance in the compiler -- written in C++ -- and the resulting code is difficult to follow, hard to maintain, and confusing to debug. I've been doing my best to gradually eliminate code like this. I'm opposed to enabling the same sort of bad patterns in C# unless there is an enormously compelling benefit to doing so.
The task of describing that enormous benefit in a compelling way is laid upon the people who want the feature, not upon the people who would have to implement it. So what's the compelling benefit?
Example of code, where this could help:
public class SpecialDataRow<T> : T where T : DataRow
{
public int SpecialFactor { get; set; }
}
This would enable making 'special' rows from DataRow and also from any derived DataRows (like typed dataset generated ones)
I do not see any other way how to code such a class
What would be so useful about this?
Remember that despite the name, generics were never intended to support generic programming.
To support a feature like this, they'd have to make some pretty dramatic changes to the CLR.
You'd need to define a class that derives from a type that doesn't even exist at compile-time.
Why should they jump through such hoops and pretty fundamentally compromise their type system just to add this feature? Is it worth it?
If you think so, tell them why. Write feedback on connect.microsoft.com telling them why this feature is so fundamental that it must be added.
C++ templates cannot be compared to C# generics. C++ templates are pre-processed like macros, while generics in .NET are handled by the runtime.
But there are other people who know a lot more about that than me...
References in C# are quite similar to those on C++, except that they are garbage collected.
Why is it then so difficult for the C# compiler to support the following:
Members functions marked const.
References to data types (other than string) marked const, through which only const member functions can be called ?
I believe it would be really useful if C# supported this. For one, it'll really help the seemingly widespread gay abandon with which C# programmers return naked references to private data (at least that's what I've seen at my workplace).
Or is there already something equivalent in C# which I'm missing? (I know about the readonly and const keywords, but they don't really serve the above purpose)
I suspect there are some practical reasons, and some theoretical reasons:
Should the constness apply to the object or the reference? If it's in the reference, should this be compile-time only, or as a bit within the reference itself? Can something else which has a non-const reference to the same object fiddle with it under the hood?
Would you want to be able to cast it away as you can in C++? That doesn't sound very much like something you'd want on a managed platform... but what about all those times where it makes sense in C++?
Syntax gets tricky (IMO) when you have more than one type involved in a declaration - think arrays, generics etc. It can become hard to work out exactly which bit is const.
If you can't cast it away, everyone has to get it right. In other words, both the .NET framework types and any other 3rd party libraries you use all have to do the right thing, or you're left with nasty situations where your code can't do the right thing because of a subtle problem with constness.
There's a big one in terms of why it can't be supported now though:
Backwards compatibility: there's no way all libraries would be correctly migrated to it, making it pretty much useless :(
I agree it would be useful to have some sort of constness indicator, but I can't see it happening, I'm afraid.
EDIT: There's been an argument about this raging in the Java community for ages. There's rather a lot of commentary on the relevant bug which you may find interesting.
As Jon already covered (of course) const correctness is not as simple as it might appear. C++ does it one way. D does it another (arguably more correct/ useful) way. C# flirts with it but doesn't do anything more daring, as you have discovered (and likely never well, as Jon well covered again).
That said, I believe that many of Jon's "theoretical reasons" are resolved in D's model.
In D (2.0), const works much like C++, except that it is fully transitive (so const applied to a pointer would apply to the object pointed to, any members of that object, any pointers that object had, objects they pointed to etc) - but it is explicit that this only applies from the variable that you have declared const (so if you already have a non-const object and you take a const pointer to it, the non-const variable can still mutate the state).
D introduces another keyword - invariant - which applies to the object itself. This means that nothing can ever change the state once initialised.
The beauty of this arrangement is that a const method can accept both const and invariant objects. Since invariant objects are the bread and butter of the functional world, and const method can be marked as "pure" in the functional sense - even though it may be used with mutable objects.
Getting back on track - I think it's the case that we're only now (latter half of the naughties) understanding how best to use const (and invariant). .Net was originally defined when things were more hazy, so didn't commit to too much - and now it's too late to retrofit.
I'd love to see a port of D run on the .Net VM, though :-)
Mr. Heljsberg, the designer of the C# language has already answered this question:
http://www.artima.com/intv/choicesP.html
I wouldn't be surprised if immutable types were added to a future version of C#.
There have already been moves in that direction with C# 3.0.
Anonymous types, for example, are immutable.
I think, as a result of extensions designed to embrace parallelism, you will be likely to see immutability pop up more and more.
The question is, do we need constness in C#?
I'm pretty sure that the JITter knows that the given method is not going to affect the object itself and performs corresponding optimizations automagically. (maybe by emitting call instead of callvirt ?)
I'm not sure we need those, since most of the pros of constness are performance related, you end up at the point 1.
Besides that, C# has the readonly keyword.