Passing a parameter so that it cannot be changed – C#

I read this requirement of not allowing a user to change the value of a property passed as a parameter to a method. In C++, as far as I could recall (it’s been over 10 yrs, so I had to refresh memory), you can pass ‘const’ to a function parameter and this ensures that the parameter cannot be changed inside the scope of the function.

There’s no such direct way of doing this in C#, but that does not mean it cannot be done!!

Ok, so this ‘not-so-direct’ technique depends on the type of the parameter – a simple property or a collection.

Parameter as a simple property: This is quite easy (and you might have guessed it already). Bulent Ozkir clearly explains how this can be done here.

Parameter as a collection property: Obviously the above does not work if the parameter is a collection of some type. Let’s dig-in. Suppose I need to create a collection of type KeyTitle as defined below.

   1: public class KeyTitle
   2: {
   3:     public int Key { get; set; }
   4:     public string Title { get; set; }
   5: }

My class is declared as below:

   1: public class Class1
   2: {
   3:     public Class1()
   4:     {
   5:         MyKeyTitleList = new List<KeyTitle>();
   6:     }
   7:         
   8:     public List<KeyTitle> MyKeyTitleList { get; set; }
   9:     public ReadOnlyCollection<KeyTitle> ReadonlyKeyTitleCollection
  10:     {
  11:         // .AsReadOnly creates a ReadOnlyCollection<> type
  12:         get { return MyKeyTitleList.AsReadOnly(); }
  13:     }
  14: }

See the .AsReadOnly() method used in the second property? As MSDN says it:

“Returns a read-only IList<T> wrapper for the current collection.”

Knowing this, I can implement my code as:

   1: public static void Main()
   2: {
   3:     Class1 class1 = new Class1();
   4:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 1, Title = "abc" });
   5:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 2, Title = "def" });
   6:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 3, Title = "ghi" });
   7:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 4, Title = "jkl" });
   8:  
   9:     TryToModifyCollection(class1.MyKeyTitleList.AsReadOnly());
  10:  
  11:     Console.ReadLine();
  12: }
  13:  
  14: private static void TryToModifyCollection(ReadOnlyCollection<KeyTitle> readOnlyCollection)
  15: {
  16:     // can only read
  17:     for (int i = 0; i < readOnlyCollection.Count; i++)
  18:     {
  19:         Console.WriteLine("{0} - {1}", readOnlyCollection[i].Key, readOnlyCollection[i].Title);
  20:     }
  21:     // Add() - not allowed
  22:     // even the indexer does not have a setter
  23: }

The output is as expected:

image

The below image shows two things. In the first line, I’ve tried to access an element in my read-only collection through an indexer. It shows that the ReadOnlyCollection<> does not have a setter on the indexer. The second line tells that there’s no ‘Add()’ method for this type of collection.

image

The capture below shows there’s no ‘Remove()’ method either, there-by eliminating all ways of modifying a collection.

image

Mission accomplished… right?

Now, even if you have a collection of different type, all you need to do is to somehow cast (used loosely) it to a List<> and then do a .AsReadOnly() to get a ReadOnlyCollection of your custom collection type. As an example, if you have an IDictionary<int, string>, you can create a List<T> of this type with a wrapper class (KeyTitle in our case).

   1: public IDictionary<int, string> MyDictionary { get; set; }
   2:  
   3: public ReadOnlyCollection<KeyTitle> ReadonlyDictionary
   4: {
   5:     get
   6:     {
   7:         return (from item in MyDictionary
   8:                 select new KeyTitle
   9:                             {
  10:                                 Key = item.Key,
  11:                                 Title = item.Value,
  12:                             }).ToList().AsReadOnly();
  13:     }
  14: }

Cool huh?

Just one thing you need to know about the .AsReadOnly() method is that the only way to modify your ReadOnlyCollection<> is to modify the original collection. So doing:

   1: public static void Main()
   2: {
   3:     Class1 class1 = new Class1();
   4:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 1, Title = "abc" });
   5:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 2, Title = "def" });
   6:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 3, Title = "ghi" });
   7:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 4, Title = "jkl" });
   8:     TryToModifyCollection(class1.MyKeyTitleList.AsReadOnly());
   9:  
  10:     Console.WriteLine();
  11:  
  12:     class1.MyKeyTitleList.Add(new KeyTitle { Key = 5, Title = "mno" });
  13:     class1.MyKeyTitleList[2] = new KeyTitle{Key = 3, Title = "GHI"};
  14:     TryToModifyCollection(class1.MyKeyTitleList.AsReadOnly());            
  15:  
  16:     Console.ReadLine();
  17: }

Gives me the output of:

image

See that the second element’s Title is changed to upper-case and the fifth element also gets displayed even though we’re still looping through the same ReadOnlyCollection<KeyTitle>.

Verdict: Now you know of a way to implement ‘Method(const param1)’ in your code!

5 Comments

  • Could you post the code for TryToModifyCollection()? I think you may be doing something very dangerous here, which is violating the interface contract. If you give TryToModifyCollection() to another programmer to implement, and it takes an IList as a parameter, you are guaranteeing him (by the contract) that the list is, in fact, mutable.

    That was the great thing about const in C++; you were guaranteed by the compiler that you cannot even attempt to modify the collection. (And because of this, you could pass in a mutable collection and C++ would handle making it const for you.

    I haven't done C# in a few years, but if I really needed to guarantee no modification, I would split the interface to my collection, perhaps creating an IReadOnlyColletion interface, and passing that in to the function. Now the programmer implementing TryToModifyCollection knows from the outset that she cannot change the collection.

  • Gah, I just discovered that your text boxes were scrollable, and that you did indeed implement the interface contract correctly. My mistake.

    Great post!

    David

  • @David, yes it was by design to make the method accept a ReadOnlyCollection type and not a List and thanks for bringing up that point.

  • Perhaps a naive question here, but what are the advantages of accepting a ReadOnlyCollection instead of simply accepting IEnumerable?

  • Yes you can, but here are the differences:

    static IEnumerable Test(IEnumerable stringEnumerable)
    {
    IList editableCollection = stringEnumerable as IList;
    editableCollection.Add("add");
    return editableCollection.AsEnumerable();
    }

    static IList TestR(ReadOnlyCollection readOnlyCollection)
    {
    IList editableCollection = readOnlyCollection as IList;
    editableCollection.Add("asdf");
    return editableCollection;
    }

    The first method will work just fine, but the second one will throw an exception (Collection is read-only). Gives you one more level of safety.

    Arun

Comments have been disabled for this content.