18

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#?

Incognito
  • 16,567
  • 9
  • 52
  • 74
CJ7
  • 22,579
  • 65
  • 193
  • 321
  • possible duplicate of [Why const parameters are not allowed in C#](http://stackoverflow.com/questions/3263001/why-const-parameters-are-not-allowed-in-c) – Incognito Aug 08 '10 at 09:01
  • 2
    That question asks why const parameters are not allowed. My question asks whether a read-only parameter is possible in C#. I don't consider it a duplicate. – CJ7 Aug 08 '10 at 09:27

5 Answers5

19

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
  • 278,309
  • 50
  • 514
  • 539
  • Can you elaborate? Would I need to make MyObject implement the IEnumerable interface? – CJ7 Aug 08 '10 at 09:18
  • 1
    `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 Aug 08 '10 at 09:25
  • +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 Aug 08 '10 at 09:41
7

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.

Community
  • 1
  • 1
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • Although another answer is essentially the same, I really appreciate the extra effort to explain it so well in a way that a CPP programmer would understand. This answer is an outstanding effort which is very helpful. – shawn1874 Sep 12 '15 at 00:19
  • @shawn1874 - thanks, what a great bit of feedback! :) – Daniel Earwicker Sep 12 '15 at 19:54
3

This is not possible in C#.

You can prevent this by passing in an immutable object.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
0

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
  • 380,855
  • 102
  • 628
  • 825
  • You mean like a clone of the original object? – CJ7 Aug 08 '10 at 09:11
  • Yes, exactly, if that is feasible. If you do, then changes doesn't matter. – Lasse V. Karlsen Aug 08 '10 at 11:06
  • What is the overhead of making your class implement the ICloneable interface? – CJ7 Aug 08 '10 at 12:35
  • 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 Aug 08 '10 at 12:58
0

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: Deep cloning objects

Community
  • 1
  • 1
Saher Ahwal
  • 9,015
  • 32
  • 84
  • 152
  • 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 Aug 08 '10 at 09:15
  • I would prefer a solution that doesn't increase memory costs. – CJ7 Aug 08 '10 at 09:15
  • 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 Aug 08 '10 at 09:16
  • 1
    @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 Aug 08 '10 at 09:18
  • @Daniel: wouldn't it be a copy of the whole object, not just the reference? – CJ7 Aug 08 '10 at 09:20
  • @Daniel: I was hoping a modifier such as const or readonly could be used, which would alert the next coder who saw the method. – CJ7 Aug 08 '10 at 09:21
  • @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 Aug 08 '10 at 09:38
  • @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 Aug 08 '10 at 09:39