Introduction
In this article, we will discuss four new and very important features that have been introduced in the later versions of C#. Developers coming from an older version like C# 2.0 or perhaps those learning C# might find this article useful.
Background
C# is a language that is improving at a very fast pace. It is getting more and more features with every release. It came years after languages like C++ and Java but managed to surpass them in terms of language features and developer productivity.
In this small tutorial article, I will try to explain four very important features of C# programming language in my own way. This might not be very useful for those who are already aware of these features, but this article is meant for beginners who are still in the phase of learning C#.
We will be discussing the following four features of C# in this article. We will also write a sample application for each to see them in action.
- Extension Methods
- Named and Optional Parameters
- Object Initializers
- Anonymous types
Using the Code
Extension Methods
Open Closed Principle(OCP) states that we should design our types in such a way that it should be open for extension, but closed for modification. Extension methods in C# can be thought of as a mechanism to implement the OCP for user defined types or even the primitive types and the types defined in the framework.
Extension methods allow us to extend an existing type by adding additional methods and functionality to an existing type without needing to change the code of that type (we might even not have code in most cases). Prior to the existence of extension methods, developers used to create their own types and either inherit or contain the existing type inside these types. These new types were more of the wrappers
on the existing types rather than the actual extensions of these types.
For example, if we needed to have the functionality of getting the negative of an integer, then I will have to wrap my integer in a custom type and then use this custom type to perform the operation.
struct MyInt
{
int value;
public MyInt(int val)
{
this.value = val;
}
public int Negate()
{
return -value;
}
}
static void Main(string[] args)
{
MyInt i = new MyInt(53);
Console.WriteLine(i.Negate());
}
Now this approach did work but the problem is that we are not at all extending the existing type. We are creating a new type all together that is wrapping the existing type and then providing us the desired functionality.
So if we really want to extend a method, we should be able to add this method Negate
in the int
type only and everyone should be able to call this method on int
type only. No need to create another type to have this additional functionality. Extension methods provide just the same thing. Using extension methods, we can write the custom functionality and can hook them with the existing types so that they can be used on those types.
To create an extension method, we need to take the following steps:
- Define a
static
class. - Define a
public static
function in this class with the name desired as the new method name and return value as per the functionality. - Pass the parameter of type that you want to extend. The important thing here is to put a
this
keyword before the parameter to tell the compiler that we need to extend this type and we are not really expecting this type as argument.
So let's try to add this Negate
function in the int
type.
static class MyExtensionMethods
{
public static int Negate(this int value)
{
return -value;
}
}
static void Main(string[] args)
{
int i2 = 53;
Console.WriteLine(i.Negate());
}
Now using extension method, let us define the methods on some existing type. Now what if we want the new method to accept some parameters. Well to do this, we can define additional parameters after the first parameter that is of the type to be extended (used with this
keyword. Let's define one more function in int
called Multiply
to see this in action.
static class MyExtensionMethods
{
public static int Negate(this int value)
{
return -value;
}
public static int Multiply(this int value, int multiplier)
{
return value * multiplier;
}
}
static void Main(string[] args)
{
int i3 = 10;
Console.WriteLine("Passing arguments in extension methods: {0}", i3.Multiply(2));
}
Now, there are two points that need to be remembered before implementing extension methods:
- First of which is that the extension methods can only access the
public
properties of the type. - The extension method signature should not be the same as an existing method of the type.
- The extension method for a type can only be used if the namespace that contains the extension method is in scope.
- In case we define extension methods in such a way that it overloads an existing method of the original type and the calls are getting ambiguous, then the overload resolution rule will always choose the instance method over the extension method.
- If there is some ambiguity between two extension methods, then the method containing more specific arguments will get called.
If we keep the following point in mind, we could really utilize the extension methods to better design and extend the types and provide better abstraction of the type. LINQ
uses extension methods heavily. It is highly recommended to look at how LINQ
used extension methods in conjunction with LAMBDA
expression.
Note: Information on Lambda expressions can be found here: A Beginner's Tutorial on Basics of Delegates, Anonymous Functions and Lambda Expressions in C#[^].
Optional Parameters
Now before we go ahead and start talking about Named and positional parameters, let us talk a little about optional parameters. In older versions of C#, it was not possible to create optional parameters for functions, i.e., if I need to have default value of a parameter in a function, the only way to do that was the function overloading.
Lets say we want to define a function Multiply
in which the caller can pass two numbers and get the result back. But if the user wants to pass only one parameter, then that is allowed too and the function will Multiply
this number with 1 and return the value. Now in older versions of C#, this is done using function overloading as:
private static int Multiply(int num1, int num2)
{
return num1 * num2;
}
private static int Multiply(int num1)
{
return Multiply(num1, 1);
}
static void Main(string[] args)
{
Console.WriteLine(Multiply(2));
}
Now this provided the desired result but the problem here is that as the number of function arguments increase, the number of overloaded functions will also increase to provide all possible combinations. Having a lot of overloaded version of function is a maintenance nightmare.
Now to solve this problem, C# introduced the provision of having optional parameters in functions. So using optional parameters, the above function will look like:
private static int Multiply2(int num1, int num2 = 1)
{
return num1 * num2;
}
static void Main(string[] args)
{
Console.WriteLine(Multiply2(2));
}
The only limitation of having optional parameters is that the default parameters should always come after all the mandatory parameters in a function.
Named Parameters
Now having the possibility of having the optional parameter is great but there is one small problem, if we have multiple optional parameters in a function. Let's say we have a function like this:
private void TestFunction(int one, int two, int three = 3, int four = 4, int five = 5)
{
}
Now we want to pass the values of four
and five
but want to use the default value of three
. This would not be possible under normal conditions. To perform this operation, we need to use the named parameters. Named parameters gives us the possibility of passing specific function argument with their names. So let's try to call this function.
TestFunction(one: 11, two: 22, four: 44, five: 55);
Now calling the function like above, we use the default value of three
and at the same time, pass the values for four
and five
, which are the optional parameters defined after three
.
We have also used the named argument for variable one
and two
, but since we are passing one
and two
at their respective parameter positions, we can omit the names from them and thus, they will simply be passed as positional parameters and four
and five
as named parameters.
TestFunction(11, 22, four: 44, five: 55);
Object Initializers
Object initializers in C# provides us with a simple way to initialize all the properties of objects at the time of construction only. Let's say we have a simple class with some public
properties as:
class Book
{
public int BookID { get; set; }
public string BookName { get; set; }
public string AuthorName { get; set; }
public string ISBN { get; set; }
}
Now let us create the object of this class using object initializers as:
static void Main(string[] args)
{
Book book = new Book
{
BookID = 1,
BookName = "MVC Music Store Tutorial",
AuthorName = "Jon Galloway",
ISBN = "NA"
};
}
A Note on Automatic Properties
Now the way I defined the class properties might be new for the guys using C# 2.0 or below. The properties are defined as automatic properties. If any variable of our class is only acting as a data store and has no validation logic before setting or getting it, then we can completely skip the creation of the variable and directly create the property. The compiler will take care of generating a variable for this property at run-time.
Implicitly Typed Variables and Anonymous Types
In C# 3.0 and above, we can define a variable without specifying its actual type provided that the variable is being assigned with some value. These variables are implicitly type, i.e., the type of the variable is the type of the variable that is being assigned to it.
var var1 = new Dictionary<string, Book>();
var var2 = "some random string";
var var3 = Multiply(3, 4);
Now the type of the var1
is Dictionary<string, Book>
because it is being created as that. The type of var2
is string
because a string
is being assigned to it and type of var3
is int
because the function is returning a int
.
var
does not define a dynamic variable that can be assigned with any type. It is just a syntactic sugar so that we can skip defining the type of variable in local scope.
var
is just a syntactic sugar, Having said that, there is one more thing that is important. We should try not to use the var
keyword for declaring local variables because for which we can define explicit types. The reason for this is that it reduced readability of code. Now the question would be when to use var
?
We should use var
only when we have to. The only place we need to use var
is with Anonymous types, i.e., types that are created on the fly and their type name is not available at compile time. LINQ queries often creates the need of creating Anonymous types and this is the only place we should use implicitly types variable, i.e., var
.
Let us create a simple anonymous type, assign it and use it using implicitly typed variable.
var aVar = new { Name = "Rahul", Age = 30 };
Console.WriteLine("{0} is {1} years of age", aVar.Name, aVar.Age);
Note: Anonymous type mandates the use of var
keyword. For all other scenarios, it is not recommended to use var
because of the above mentioned reduced readability problem. If LINQ
projections are being used, then the use of var
keyword is an absolute must.
Point of Interest
In this article, we talked about some features of C# that were introduced in C# 3.0. This article is meant for absolute beginners who are not aware of these features of C# and is written from an absolute beginner's perspective only. I highly recommend looking at the code to get a full understanding of all these features. I hope this has been informative.
History
- 5th April, 2013: First version