locked
Constness. C# doesn't seem to support it. RRS feed

  • Question

  • I want to return a collection to a caller that he cannot modify (I can use the ReadOnlyCollection wrapper).  But he can still modify the references inside the collection, so I haven't really gained very much.  Instead I return a ReadOnlyCollection of cloned objects, so that even if he does modify them it will have no effect on my class.  In another case I have two properties, each returns a bitmap.  I don't want the user to modify the bitmaps.  Obviously I don't want to clone them as well because that's not a very quick operation!

    Given that it's generally not such a good idea to expose internals outside of a class and that in C++ we use constness when we don't want something to be changeable, how can we achieve this with c#?  It seems the only solution is to create new interfaces and to return those.  A lot of extra work!  What happened to the concept of const in c# during its design?  Anyone?
    Wednesday, September 23, 2009 8:09 AM

Answers

All replies

  • The other term for "constness" is immutability. Immutability is very useful concept for Domain Driven Development. There are languages that support notion of immutable values at the very low level, like F#, but unfortunately, C# does not support this.
    The only possible ways I see is "cloning" or creation of your custom wrappers.
    Vitaliy Liptchinsky http://dotnetframeworkplanet.blogspot.com/
    Wednesday, September 23, 2009 8:20 AM
  • I agree with Vitaliy. Use composition and create a read-only representation of your classes by simply exposing property get accessors and limiting access to methods which modify state. 
    Wednesday, September 23, 2009 8:29 AM
  • Yes, I understand this.  Ultimately I have to write my classes twice. Once so the owner can make modifications and again for instances where I want to return an immutable version.  But really my question was more about the design decision to limit the concept of constness in c#.   I'm fine with comments to the effect that the caller should not modify the collection (most programmers can read), but I like the safety of a compiler check with the caller having to explicitly cast the constness away if he really wants to modify the objects inside.
    Wednesday, September 23, 2009 8:52 AM
  • The only reason I know of is that the CLI does not support it. You've probably already seen this FAQ item and this blog post.
    • Marked as answer by RobinsonUK Wednesday, September 23, 2009 11:32 AM
    Wednesday, September 23, 2009 10:36 AM
  • Thanks.  Some interesting discussion there I'd missed before.
    Wednesday, September 23, 2009 11:32 AM
  • Different philosophy.  The const keyword in C/C++ doesn't mean beans, it is in no way enforced by the runtime.  Not even in the compiler, a quick const_cast<>() and your attempt to protect your collection is history.  C# is more of a language that doesn't make promises it can't keep.  Which is important in the managed language approach, back doors must be closed or be wide open, no such thing as "undefined behavior".  Since true reference const-ness cannot be enforced, there's little point in burning up a keyword on it.

    Hans Passant.
    Wednesday, September 23, 2009 11:37 AM
    Moderator
  • Yeah, I always had problems with const-correctness in C++. Hard as I would try to make everything const-correct, someone would always come along and subvert it by casting away the constness somewhere (usually because they wanted to pass something to a function that didn't expect it to be const).

    In the end, I more-or-less gave up on it as unsustainable in practice (in C++).
    Wednesday, September 23, 2009 12:17 PM
  • I disagree about the C++ constness. In my >10 years of C++, I found mastering constness a very powerful tool.

    It's true that the constness can be cast away; however, a "const_cast" is one of the biggest flags for "this needs a code review" unless it has a very clear comment indicating how it is safe. A const_cast can easily cause undefined behavior.

    Legacy APIs are a problem. It's not possible to have correct constness at any level unless the lower levels already support it. I spent several years working on Boost, and const-correctness was one of the high standards that Boost libraries were held to.

    Constness, when used correctly and consistently, helps document the interface contract and can greatly help compiler optimization. In C#, the compiler optimization is not as big of an issue, but I do sometimes miss the interface contract part.

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    Wednesday, September 23, 2009 1:07 PM
  • I agree it would be a powerful tool if only one could get everyone to do it properly.

    Unfortunately, I found that to be impossible. :(

    Incidentally, I used the Boost libraries for many years. Great stuff. :)
    Wednesday, September 23, 2009 2:00 PM
  • Ok, taking my C++ hat off, I'm warming to the concept of Immutability being superior to that of "constness".  I made a very simple example of how easy it is to ensure such a contract can never be broken.  It turns out the interface can be read only and the implementation can add a "set" in c#, so:

            /// <summary>
            /// Read only interface.
            /// </summary>
            public interface Ia
            {
                int t { get; }
            }
    
            /// <summary>
            /// Class we want to make readonly.
            /// </summary>
            public class A : Ia
            {
                private int _t = 0;
    
                public int t
                {
                    get
                    {
                        return _t;
                    }
    
                    set
                    {
                        _t = value;
                    }
                }
            }
    
    ......
    
            A myA = new A();
    
            myA.t = 5; 
    
            Ia myIa = myA;
    
            myIa.t = 6;    // Compiler error here.
    

    This kind of thing is pretty tidy in my opinion.

    Wednesday, September 23, 2009 6:33 PM