Reader Level:
ARTICLE

Passing Const Parameter to Functions in C#/C++/VB Compared

Posted by Bulent Ozkir Articles | C# Language August 30, 2001
Parameter passing to a function is extremely important in all programming languages. The desire to keep the passed parameter intact forced the compiler designers to add various keywords to the programming languages.
  • 0
  • 0
  • 52709

Description 

Parameter passing to a function is extremely important in all programming languages. The desire to keep the passed parameter intact forced the compiler designers to add various keywords to the programming languages. This is an issue that the library developers keep quite much, since it gives relief to consumers of library, feeling that the object they pass is intact in return. C/C++ has const and VB has ByVal keyword to achieve this. Indeed, in C/C++ this is a contract rather than a force. Since you can violate constant value and break into its contents anytime in your module. Let's remember the old C style ugly pointer notations and the const keyword, and how we can ignore constant value inside our custom method. The declaration of pch as const assures the caller to MetConpp method that pch object contents cannot be changed by the called function, namely inside MetConpp. 

I also fleshed your memories to recognize the various const declarations in various languages. 

// C style const pointers
#include "stdafx.h"
// method changes passed const pointer to const object
void MetConpp(const char* const pch)
{
char* ppch = (char*) pch;
*ppch = 'X';
}
int main(int argc, char* argv[])
{
char ch1 = 'c';
char ch2 = 'C';
const char *pch1 = &ch1; // const object
char *const pch2 = &ch1; // const pointer
const char *const pch3 = &ch1; // const pointer and object
//*pch1 = 'A'; // ERROR
pch1 = &ch2; // OK
*pch2 = 'A'; // OK
//pch2 = &ch2; // ERROR
//*pch3 = 'A'; // ERROR
//pch3 = &ch2; // ERROR
MetConpp(pch3);
// break constness - pch3 now contains X
return 0;
}

C++ made our life easier by introducing references. In C++, we can define a const reference to a method as in the following example. When you pass a const reference it is assumed that the inner statements do not modify the passed object. As we know in C++, references are aliases to variables though they represent memory addresses and they are coded as regular variables in terms of syntax. References are syntactic comfort to the life of developers when compared to ugly seeming  pointers : 

// C++ - const references
#include <iostream.h>
void methodconst(const int&);
void main(void)
{
int iLocal = 10;
methodconst(iLocal);
cout << iLocal;
// we expect 12
}
void methodconst(const int& iParam1)
{
// preprocessor handles constness in C++
// unluckily there is no const parameters to methods in .NET Framework for ANSI C++
// int& iAlias = iParam1; // ERROR - preprocessor
//'initializing' : cannot convert from 'const int' to 'int &' Conversion loses qualifiers
// error C2166: l-value specifies const object
// iParam1 = 11; // ERROR - preprocessor
// error C2166: l-value specifies const object
int* iLoc1 = (int*)&iParam1; // OK-break contract -delude preprocessor
(*iLoc1)++;
int* iLoc2 = const_cast<int*>(&iParam1); // OK - break contract-delude preprocessor
(*iLoc2)++;
}

Let's dive into C# now. Implicit [in] parameter modifier provides that value types are not modified in a method. This is really what we want However, in C# the implicit [in] parameter modifer has no effect for references. (Do not confuse this term with the one in C++. References are the name given to instances of classes in C#. all objects are references in C#. Other than that there are value types. Refer to a C# book if you want to learn more about these topics.). But it is not a problem indeed, since as we saw in the C/C++ example, const& is a contract indeed. You can violate this contract on your side outrageously.

There are 2 options however you can do. We can devise a means, as String and StringBuilder counterparts behave.String class does not have a setters and it is immutable with the compiler support.However StringBuilder is mutable class and its objects are references. When we want to pass a const string in the case we have a StringBuilder, we copy the StringBuilder contents to String and pass it as an [in] argument. When we assign something new to a string, a new string is created and its address is reassigned to string again. But String class has an advantage. .NET Framework helps String objects acts as if they are value types, though it is actually a reference. So we can use a similar technique and create our custom immutable class counterparts for our custom classes on demand. Another option is to create Struct counterparts to classes if we need constness as a must. I prefer struct style since structs are leightweight classes, and structs are value types. 

In the following sample we declared and defined MyClass (mutable class), OtherMyClass (immutable class), StructMyClass (struct). All the tree objects store the same data and the two latter ones are redundant and actually created for providing constness. OtherMyClass and StructMyClass have constructors of MyClass for easy object copying. To expliclity show whether the parameters are changed, we passed MyClass object to methodconst, OtherMyClass object to methodconst2 and StructMyClass variable to methodconst3. Only methodconst changed the variable's value. So we can say that our remedies seem to work, though they cause plethora. But you know what you will do, when you need constness in C#. 

// C#- [in] parameter modifier illustration
// compile with :
// csc /out:const1.exe const1.cs
/* sample output:
MyClass:11
OtherMyClass:11
StructMyClass:11
*/
using System;
class MyClass
{
public int param1;
}
class OtherMyClass
{
public OtherMyClass(MyClass ms)
{
this.param1 = ms.param1;
}
private int param1;
public int Param
{
get
{
return this.param1;
}
/* // no setters
set
{
this.param1 = value;
}
*/
}
}
struct StructMyClass
{
public int param1;
public StructMyClass(MyClass ms)
{
param1 = ms.param1;
}
}
class TestApp
{
public static void Main()
{
MyClass cls1 =
new MyClass();
cls1.param1 = 10;
methodconst(cls1);
Console.WriteLine("MyClass:" + cls1.param1);
// Surprise-changed 11
// immutable class - only getters
OtherMyClass cls2 = new OtherMyClass(cls1);
methodconst2(cls2);
Console.WriteLine("OtherMyClass:" + cls2.Param);
// ok 11
StructMyClass struct1 = new StructMyClass(cls1);
methodconst3(struct1);
Console.WriteLine("StructMyClass:" + struct1.param1);
// ok 11
}
public static void methodconst(/*[in]*/ MyClass cls1)
{
cls1.param1++;
}
public static void methodconst2(/*[in]*/ OtherMyClass cls2)
{
// the cls2 is constant in terms of param1 and Param. Why?
// Because they are immutable members...
// cls2.Param++;
// ERROR: Property or indexer 'OtherMyClass.Param' cannot be assigned to-
// it is read only
// cls2.param1++;
// ERROR: 'OtherMyClass.param1' is inaccessible due to its protection level
}
public static void methodconst3(/*[in]*/ StructMyClass struct1)
{
struct1.param1++;
// ERROR: 'StructMyClass.param1' is inaccessible due to its protection level
}
}

COMMENT USING

Trending up