Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

No const objects

How do you deal with the lack of the "const" qualifier in .NET?

For example, I have a class which contains as member data a huge XML tree:

public class MyClass
{
    System.Xml.XmlDocument m_xml_document;
}

I want to have an accessor, that lets users of this class *read* the document but *not alter* it in any way.

In C++ I would do it by defining an accessor (a property) that returns a const reference, like this:

public class MyClass
{
    System.Xml.XmlDocument m_xml_document;
    const System.Xml.XmlDocument& get_document()
    {
        return m_xml_document;
    }
}

.NET doesn't appear to support const references: to return a non-readable document reference, it seems that what I must do is make a clone (a copy) of the document, and return a writable clone (which guarantees that the original document remains untouched).

This is a huge performance hit ... perhaps it's the one reason why .NET is slower than unmanaged C++!

Is there any work-around??

Christopher Wells
Thursday, October 21, 2004

Maybe I missed the point, but can't you use the readonly modifier, in lieu of the C++ const ?

For example:

public class MyClass
{
    public readonly System.Xml.XmlDocument m_xml_document;
}

Of course, using the readonly modifier means you have to set up your XmlDocument in your constructor, as after that it becomes readonly.

Nemesis
Thursday, October 21, 2004

The point is that I want MyClass to be able to edit the document (by calling "non-const" methods of the XmlDocument instance).

I want classes which own a MyClass instance to be able to inspect the data (the document) that is managed (edited) by the MyClass instance, but not to be able to edit it (only the MyClass instance itself should be able to edit it).

.NET doesn't have const methods and const references, so I'm defeated.

Christopher Wells
Thursday, October 21, 2004

Okay, I see what you mean, I did mis-understand. My method only allows you to manipulate the document in the constructor.

Nope, I don't know a good way to do that apart from cloning, which may well be very slow.

Nemesis
Thursday, October 21, 2004

Indeed .NET doesn't provide a built-in way to have read-only access to read/write variables.

The only solution is to provide different access methods & properties: internal ones that allow R/W access, and public ones that "somehow" (see below) allow read-only access.

For value types, there's no problem since anything you get is a copy anyway, and you have to deliberately invoke some kind of setter method or property to change the value. That's easy to trap.

For ArrayList collections, you can return a read-only wrapper to the caller that isn't allowed to change their contents. This prevents the caller from adding, deleting or replacing elements, but if the elements are reference types their contents can still be changed.

In the general case of reference types, you're out of luck, though. As soon as the caller has the reference it can do anything with the object that the callee could do. You'll have to create your own read-only wrappers for all reference types whose instances you don't want changed.

Chris Nahr
Thursday, October 21, 2004

What about this as a solution (sorry about the VB)?  I tested it and think it will do what you want:

Public Class MyClass

  Private WithEvents m_XmlDoc As XmlDocument
  Private m_AllowEdits As Boolean

  Public Sub ChangeDocData()
    m_AllowEdits = True
    ' Modify the XmlDocument
    m_AllowEdits = False
  End Sub

  Public ReadOnly Property Document() As XmlDocument
    Get
      Return m_XmlDoc
    End Get
  End Property


  Private Sub DocumentEdits(ByVal sender As Object, ByVal e As System.Xml.XmlNodeChangedEventArgs)
    If Not m_AllowEdits Then Throw New Exception("Cannot change data")
  End Sub

  Public Sub New()
    m_XmlDoc = New XmlDocument
    AddHandler m_XmlDoc.NodeChanging, AddressOf DocumentEdits
    AddHandler m_XmlDoc.NodeInserting, AddressOf DocumentEdits
    AddHandler m_XmlDoc.NodeRemoving, AddressOf DocumentEdits
  End Sub

End Class

Joe Paradise
Thursday, October 21, 2004

If they only need to inspect certain aspects of items in the Xml document you might be able to build a simple facade that exposes what you want them to see.

I'm sure you already thought of that but just throwing it out there as an alternative

smallbiz
Thursday, October 21, 2004

Microsoft's suggested solution is either:

1. return a read-only wrapper around something so that all write operations throw an exception, or

2. design your data structure to be immutable, like System.String.

The problem with "const" is twofold: you can cast it away, and not every language supports the concept (and the CLR is designed to be language agnostic).

Personally, I think this is okay. I don't miss const from my C++ days at all.

Brad Wilson
Thursday, October 21, 2004

> Personally, I think this is okay. I don't miss const from my C++ days at all.

We have an XML data store ... and our data-store manager is returning a *copy* of any data that we read from it ... which works (it's safe) but it's burning memory and CPU.

Christopher Wells
Thursday, October 21, 2004

Any class in C++ has bascially two interfaces assocated with it, its const interface, and its non-const interface.

for example:

class Bar {
//...
}

class Foo {
public:
    Bar & a();
    const Bar & a() const;
    void b() const;
}

essentally defines two interfaces: the set of methods that can be executed on a Foo instance (a(), b() const), and the set of methods that can be executed on a const Foo instance (a() const, b() const). 

To simualte this in .NET, you can define a "readonly interface" that contains stubs for all your "const" methods and then have your class derive from it. Using the example above, this would be done like this:

public interface ConstBar {
//...
}

public interface ConstFoo {
    ConstBar a();
    void b();
}

public class Foo : ConstFoo {
    ConstBar ConstFoo.a() {
      //...
    }

    public void b() {
        //...
    }

    public Bar a() {
        //...
    }
}

By implicitly implementing the ConstFoo.a() method it is possible to define a non-const version as well. Calls to a() from a variable of type Foo will call the non const version, and calls to a() from a variable of type ConstFoo will call the const version.

It is worth noting, however, that this is not identical to what C++ offers. In particular C++ does this automatically for you without requiring the explicit constant type decleration. Also, C++ places some limitations on the state that a const method can modify (although the controls can be subverted by casting away constness), whereas the C# simulation does not.

Scott Wisniewski
Monday, October 25, 2004

It's probably a big design change that you can't afford, but the way we solved this (similar problem, xml data getting passed around everywhere) was by passing an immutable object instead - namely XPathNavigator. You could also use XmlNodeReader depending on your requirements. Or you could  create your own facade (as somebody already suggested).

If you can't change the code that drastically, what's wrong with the exeception method that somebody mentioned earlier (hook every change event on xmldocument and throw an exception). This would prevent developers from changing your data.

Aaron Boodman
Tuesday, November 02, 2004

*  Recent Topics

*  Fog Creek Home