views:

64

answers:

5

C#: can you make it so that a method parameter passes an object by reference but is read-only?

eg:

void MyMethod(int x, int y, read-only MyObject obj)

where obj is an object reference but this object cannot be modified during the method.

Can this be achieved in C#?

+5  A: 

No. C# has no direct analogue to C++ const (its own const is something different). A common C# pattern for this is to pass in a interface, such as IEnumerable, that does not permit modifications. You can also create an immutable copy or wrapper.

Matthew Flaschen
Can you elaborate? Would I need to make MyObject implement the IEnumerable interface?
Craig Johnston
`IEnumerable` is an example. Think of the difference between `IEnumerable` and `List`. You can clearly modify a `List`, but if you use it as an `IEnumerable` (which it implements), all you can do is access elements in order (`MoveNext`<->`Current`). You can make your own interfaces with similar read-only properties.
Matthew Flaschen
+1, this is the same as my answer, I didn't see it. But I'll leave mine there as the "long version". :)
Daniel Earwicker
+2  A: 

This is not possible in C#.

You can prevent this by passing in an immutable object.

Oded
How you pass it is different from immutability, the two are completely orthogonal in the case of objects.
Lasse V. Karlsen
@Lasse - thanks for the correction.
Oded
A: 

There is no mechanism that does this for you.

Either pass a throwaway copy, or build immutability, even if temporary, into the class itself.

Lasse V. Karlsen
You mean like a clone of the original object?
Craig Johnston
Yes, exactly, if that is feasible. If you do, then changes doesn't matter.
Lasse V. Karlsen
What is the overhead of making your class implement the ICloneable interface?
Craig Johnston
I wouldn't, that is, I would make something similar, but I wouldn't implement ICloneable. It is a broken contract. As for overhead, it's another method, and depending on how the class has been written you might have to change parts of it to accomodate cloning. Runtime overhead should be zero though.
Lasse V. Karlsen
A: 

why don't you make a copy of that object and do whatever you want with that copy while your obj remains not modified.

See here how to clone your object: http://stackoverflow.com/questions/78536/cloning-objects-in-c

Saher
One reason why you might not want to make a copy is because you're worried about the performance cost of making a copy.
Daniel Earwicker
I would prefer a solution that doesn't increase memory costs.
Craig Johnston
Another problem with that suggestion is that the coder who writes the "receiving" method may assume that the state changes they cause on the object will be permanent, when in fact they are going to be thrown away.
Daniel Earwicker
@Craig - the cost of temporary reference object allocation on the CLR is incredibly small, closer to stack allocation than C++ heap allocation. http://smellegantcode.wordpress.com/2009/03/01/the-amazing-speed-of-the-net-garbage-collector/
Daniel Earwicker
@Daniel: wouldn't it be a copy of the whole object, not just the reference?
Craig Johnston
@Daniel: I was hoping a modifier such as const or readonly could be used, which would alert the next coder who saw the method.
Craig Johnston
@Craig Johnston - "reference object" is what the CLR calls an object allocated from the GC heap, so yes, I'm talking about copying the object. The point is that you may be worrying about nothing. If it is expensive then the memory allocation won't be the expensive part - the cost is more likely to be in having to do a deep copy (which for some objects can involve modifying state external to the program, e.g. an object that encapsulates a temporary file).
Daniel Earwicker
@Craig - the type of the reference tells the coder what they can do to the object it refers to. It makes no difference whether this is done using a modifier keyword or a distinct hand-written interface. (Well, the difference is that you have to write the interface by hand, but otherwise its exactly the same).
Daniel Earwicker
A: 

If the class of the object you're passing was written by you, then you're in luck.

Ask yourself: if C# had a const feature, what operations on the object would I expect to be banned through a const reference to my class?

Then define an interface that leaves out the banned operations.

For example:

class MyClass 
{
    public string GetSomething() { ... }

    public void Clobber() { ... }

    public int Thing { get { ... } set { ... } }
}

The corresponding "const" interface might be:

interface IConstMyClass 
{
    public string GetSomething() { ... }

    public int Thing { get { ... } }
}

Now amend the class:

class MyClass : IConstMyClass
{

Now you can use IConstMyClass to mean const MyClass.

 void MyMethod(int x, int y, IConstMyClass obj)

Note: there will be those who will tell you that this isn't enough. What if MyMethod casts back to MyClass? But ignore them. Ultimately the implementor can use reflection to get around any aspect of the type system - see this amusing example.

The only option to stop reflection attacks is the trivial approach: make a totally disconnected clone.

Daniel Earwicker