January 4, 2014

Advanced CSharp Part 3 : params, ref, out and Preprocessors

In this post we shall look into some advanced features of C# that were added in the recent versions of .NET framework. Some of these features are explicitly new for any programming language, and some of them you might have seen before in other languages.

Variable Parameter List: Sometimes it might happen that you are defining a method, but you are particularly not sure how many parameters you need. It means that the parameter list can vary while you are coding. In C#, the params keyword makes it possible. For example, the following method takes two integers as parameter and returns an integer.

int AddNums(int a, int b)
{
 int total = 0;
 total = a + b;
 return total;
}
The problem with this snippet is that, you are stuck with two arguments only. May be you could define overloaded methods with increasing number of arguments. But what if someone wants to add 8 integers? Surely you would not want to construct a method that has 8 different arguments! C# brings to you an elegant solution.

int result = AddNums(4,7,25,36,8);
int result2=AddNums(7,9);  
int AddNums(params int[] n)
{
 int total = 0;
 foreach(int i in n)
 {
  total += i;
 }
 return total;
}
Instead of taking a particular number of arguments, I tell my compiler that you can expect one or more than one arguments, in that case you should store them in the array. Then inside the body of the method, you loop over the array and do what you need to do.
A matter of caution for everyone. You cannot do something like the following:

int AddNums(params int[] n,int g,string str,object ob)
{
 int total = 0;
 foreach(int i in n)
 {
  total += i;
 }
 return total;
}
When you declare a method like that, you will get compiler error saying that params should be the last argument in a method. What it really means is that if you put more arguments after params, it really gets confused as to how many arguments it should put inside the array. However the following snippet is perfectly okay!

int AddNums(int g,string str,object ob,params int[] n)
{
 int total = 0;
 foreach(int i in n)
 {
  total += i;
 }
 return total;
}
Method Parameter Modifiers: Apart from passing variable number of arguments, you can in fact play with the way arguments behave and method return type in C#. We shall look into certain keywords like ref and out.
If you are familiar with other languages, you probably know that in most cases, passing parameters to functions or methods are usually done by value. Pass by value is desirable in most cases, but it can cause you some difficulties if you as a programmer aren't careful about what you want to achieve from your code. Look at the following code and run the code.

using System;

namespace Spells
{
    class Program
    {
        public static void Main(string[] args)
        {
            int j = 15;
            Console.WriteLine("Inside Main j={0}", j);
            MyMethod(j);
            Console.WriteLine("Inside Main j={0}", j);
            Console.Read();
        }
        static void MyMethod(int x)
        {
            x += 100;
            Console.WriteLine("Inside MyMethod x={0}", x);
        }
    }
}
The value of j does not change even after you call the method and add 100 to it. If you want to keep the changed value from the calling routine, you can add the ref keyword. In that way, you pass the parameter as reference, not by value.

using System;

namespace Spells
{
    class Program
    {
        public static void Main(string[] args)
        {
            int j = 15;
            Console.WriteLine("Inside Main j={0}", j);
            MyMethod(ref j);
            Console.WriteLine("Inside Main j={0}", j);
            Console.Read();
        }
        static void MyMethod(ref int x)
        {
            x += 100;
            Console.WriteLine("Inside MyMethod x={0}", x);
        }
    }
}
Add the keyword ref at line 11 and 15. You are ready to see a persisting change in the value of j after calling the method.

There is an out keyword that is most useful when you want multiple return values from the method via the parameters. Lets see a working example.

using System;

namespace Spells
{
    class Program
    {
        public static void Main(string[] args)
        {
            int j = 15;
            double square, cube;
            MyMethod(j, out square, out cube);
            Console.WriteLine("Square of j={0} is {1} and cube is {2}", j, square, cube);
            Console.Read();
        }
        static void MyMethod(int x, out double sqr, out double cube)
        {
            sqr = x * x;
            cube = x * x * x;
        }
    }
}
Observe that the variables square and cube have been assigned the return values from the function. This is a real nice way to assign values to a parameter that you are passing from the calling routine.

Preprocessor: Essentially it means to pre process something before the compilation of the code. Preprocessor directives give directions to the compiler. A preprocessor starts with a # , you may call it hash symbol or the pound symbol. There are a handful of preprocessors like #define, #undef, #if, #else, #elif, #endif, #region, #endregion etc. Let us see a working example.

#define SPELLS
#define HELLO
using System;

namespace Spells
{
    class Program
    {
        public static void Main(string[] args)
        {
#if SPELLS
            Console.WriteLine("SPELLS has been defined");
#else
            Console.WriteLine("SPELLS not defined");
#endif
#if HELLO
            Console.WriteLine("I say Hello");
#else
            Console.WriteLine("No Hello");
#endif
            Console.Read();
        }
    }
}
The #define must be placed above any other piece of code in the file. Go ahead and place at any other place, you will get a red squiggle representing error. So what exactly this piece of code is doing? Go ahead and paste this code in your CSharp editor.  You will see that line 14 and 19 is grey. This signifies that these lines are not part of current execution. Why? Because you see, at line 1 and 2, I have defined two symbols, namely SPELLS and HELLO. The compiler treats them as a single symbol. At line 11 and 16, I check if these symbols have been defined or not. This is just like the if-else that we used to write normally. When the compiler finds the required symbol, it allows the execution of code under that symbol.

Go ahead and change the symbols to anything you like at line 1 and 2 or simply remove them completely. You will notice that line 14 and 19 have been activated, while line 12 and 17 are currently grey-ed. This perplexing features comes handy when you are debugging your code. When you are trying to ship your debugged code to another person, you may want to run the debugging regions. Again, when you are done with debugging, you may choose to run the to-be-official code.

There is a one final important thing that you should see. Copy and paste the following code in your C# editor.

using System;

namespace Spells
{
    class Program
    {
        public static void Main(string[] args)
        {
            Method1();
            Method2(4, "Hi");
            Console.Read();
        }
        #region This is Method1
        static void Method1()
        { 
            // code here
        }
        #endregion

        #region This is Method2
        static void Method2(int a, string str)
        { 
            // code here  
        }
        #endregion
    }
}
The #region and #endregion does not affect the flow of your code, it simple enhances the beauty of your editor. I have two regions around two methods. Look at line 13 and 20, the string following #region is simply a name that you prefer to name the region. Now what's so interesting about this? Go ahead and click on the small box [-] beside line 13 and 20.
Visual Studio Editor Environment

Aha! Your editor squeezed the code there and you just have a clean editor. This is how it looked in my case.
Visual Studio Editor Environment
[-] interprets to minimizing the code or collapsing the code, while [+] interprets to expanding or maximizing the code region. This feature is particularly helpful when you have a large, very large piece of code.

I hope these little features come handy to you. I also hope you have found this post helpful. In the next post we shall learn about delegates and events.

No comments:

Post a Comment