In C# 7.2, “in parameter” has been introduced which allows passing read-only reference of a variable. Before C# 7.2, we used “ref” and “out” keywords for passing the references of a variable. “Out” is meant for output only whereas ‘ref’ is meant for input and output both. However, if we had to pass a read-only reference, i.e., passing a variable as input only, then there was no direct option for that. So, in C# 7.2, “in parameter” has been introduced for this purpose.
In my previous article, I have already discussed ‘ref read-only.’ However, in the later releases of Visual Studio 2017 version 15.5, a new parameter “in parameter” has been introduced to explain the intent in a better way.
Using “in” parameter with a value type variable.
In my previous article, I used the following code snippet for ‘ref read-only’ parameter.
- static void Main(string[] args)
- {
- int a = 20, b = 30, c = 20;
- Write($"Sum of {a} and {b} and {c} is: ");
- Add(a, b, ref c);
- WriteLine($"{c}");
- }
- public static void Add(ref readonly int x, ref readonly int y, ref int z)
- {
- z = x + y + z;
- }
However, it will not work with later releases of Visual Studio 2017 version 15.5 or above versions (except Visual Studio 2017 version 15.5 Preview 1).
Now, just replace the keyword “ref read-only” by “in” and recompile the code. The code snippet after replacing “ref read-only” by “in” will look like below.
- static void Main(string[] args)
- {
- int a = 20, b = 30, c = 20;
- Write($"Sum of {a} and {b} and {c} is: ");
- Add(a, b, ref c);
- WriteLine($"{c}");
- }
- public static void Add(in int x, in int y, ref int z)
- {
- z = x + y + z;
- }
Now, it will work successfully. Following is the output for the same.
Using “in” parameter with a reference type variable.
If you are using “in” with reference type variable, then you cannot reallocate a new memory for that object, but their properties can be modified.
Changing Properties Value of Reference Type Variable passed with In Parameter
So, you can see in the above screenshot that I can assign a value for the properties of a reference type variable which has been passed as a reference using “in” parameter but we can not re-allocate a new memory for that object. Following is the complete code for the same.
- using static System.Console;
- namespace InParameterWithReferenceType
- {
- class Program
- {
- static void Main(string[] args)
- {
- Product product = new Product();
- Modify(in product);
- }
- public static void Modify(in Product product)
- {
-
- product.ProductId = 101;
- product.ProductName = "Laptop";
- product.Price = 60000;
- WriteLine($"Id: {product.ProductId} Name: {product.ProductName} Price: {product.Price}");
- }
- }
- class Product
- {
- public int ProductId { get; set; }
- public string ProductName { get; set; }
- public decimal Price { get; set; }
- }
- }
However, if I change the object from the reference type to value type, then I would not be able to assign any property value. This means in case of value type, the member of that object also becomes read-only.
Have a look at the below code.
- using static System.Console;
- namespace InParameterWithReferenceType
- {
- class Program
- {
- static void Main(string[] args)
- {
- Product product = new Product();
- Modify(in product);
- }
- public static void Modify(in Product product)
- {
-
- product.ProductId = 101;
- product.ProductName = "Laptop";
- product.Price = 60000;
- WriteLine($"Id: {product.ProductId} Name: {product.ProductName} Price: {product.Price}");
- }
- }
- struct Product
- {
- public int ProductId { get; set; }
- public string ProductName { get; set; }
- public decimal Price { get; set; }
- }
- }
It will give a compile time error.
Comparison table for In, out and ref Parameter
in
|
out
|
ref
|
Can be used to pass a variable as a reference (value type and reference type both). |
Can be used to pass a variable as a reference (value type and reference type both). |
Can be used to pass a variable as a reference (value type and reference type both). |
Only for input not for output. |
Only for output not for input. |
For input and output both. |
"in variable" must be initialized before passing it as a parameter. |
Do not require initialization before passing it as a parameter. |
ref variable must be initialized before passing it as a parameter. |
Cannot be declared inline. |
Can be declared inline. |
Cannot be declared inline. |
"in" keyword is optional while calling the method. |
"out" keyword is mandatory while calling the method. |
"ref" keyword is mandatory while calling the method. |
It is not necessary to use them in a variable before leaving the control. |
It is necessary to initialize out variable before leaving the control. |
It is not necessary to use the ref variable before leaving the control. |
The above table is just a comparison for ref, in, and out variables but in C# 7.2, a lot more capabilities have been provided to ref keyword, and its usage area has been expanded to a great extent which needs a dedicated article for the same. So, I will be coming with another article for the new enhancements appended to the ref keyword in C# 7.0 and C# 7.2.
Performance using ‘in’ Parameter
You may be wondering why I am passing a variable as a read-only reference. The reason is that if I am passing a value type variable without reference then each time a new copy will be created. So, it will take extra memory and performance will be slower.
Thus, in another way, we can say that instead of passing a copy of value type, we can pass a read-only reference of a value type variable which will make it faster and will use less memory.
For Loop Without In Parameter
Let’s have a look at the below code snippet:
In the below snippet, you can see that I am using a struct with 500 properties. It is not recommended to use a struct for the large object, but here I am using it just for showing you the performance difference.
- namespace INPerformance
- {
- class Program
- {
- static void Main(string[] args)
- {
- BigAstroObject bigAstroObject = new BigAstroObject();
- bigAstroObject.A001 = 656371600.3042M;
- bigAstroObject.A002 = 876120136.2188M;
- ……………………………………………………………………………………………
- bigAstroObject.A499 = 549329194.3128M;
- bigAstroObject.A500 = 799448442.2474M;
- for (decimal i = 0; i < 200_000_000; i++)
- {
- DoSomeThing(bigAstroObject);
- }
- }
-
- private static int DoSomeThing(BigAstroObject bigAstroObject)
- {
-
- return 0;
- }
- }
- struct BigAstroObject
- {
- public decimal A001 { get; set; }
- public decimal A002 { get; set; }
- ……………………………………………………………………………………
- public decimal A499 { get; set; }
- public decimal A500 { get; set; }
- }
- }
Refer the attachment “ForLoopWithOutInParameter.cs” for complete code of above code snippet.
Following is the snapshot for the performance of above program which is not using the “in” parameter. It means each time it is passing a new copy of the variable.
For Loop With In Parameter
Now, run the same code using “in” parameter. Below is the code snippet of the same.
- namespace INPerformance
- {
- class Program
- {
- static void Main(string[] args)
- {
- BigAstroObject bigAstroObject = new BigAstroObject();
- bigAstroObject.A001 = 656371600.3042M;
- bigAstroObject.A002 = 876120136.2188M;
- ……………………………………………………………………………………………
- bigAstroObject.A499 = 549329194.3128M;
- bigAstroObject.A500 = 799448442.2474M;
- for (decimal i = 0; i < 200_000_000; i++)
- {
- DoSomeThing(in bigAstroObject);
- }
- }
-
- private static int DoSomeThing(in BigAstroObject bigAstroObject)
- {
-
- return 0;
- }
- }
- struct BigAstroObject
- {
- public decimal A001 { get; set; }
- public decimal A002 { get; set; }
- ……………………………………………………………………………………
- public decimal A499 { get; set; }
- public decimal A500 { get; set; }
- }
- }
Refer the attachment “ForLoopWithInParameter.cs” for complete code of the above code snippet.
Following is the snapshot of the performance of the above program which is using the “in” parameter. It means each time it is not passing a new copy of the variable, rather, it is passing a read-only reference of the same copy.
Thus, you can compare and see in the above screenshots what is the performance difference if I am using the “in” parameter. This performance may differ on your machine, but you will undoubtedly get better performance with ‘in’ parameter.
Now, I am going to do some parallel programming and trying to measure performance gain for in parameter.
Parallel For Loop Without In Parameter
Let’s have a look at the below code snippet which is using “Parallel.For()” but passing parameter without in (passing a new copy each time).
- namespace INPerformance
- {
- class Program
- {
- static void Main(string[] args)
- {
- BigAstroObject bigAstroObject = new BigAstroObject();
- bigAstroObject.A001 = 656371600.3042M;
- bigAstroObject.A002 = 876120136.2188M;
- ……………………………………………………………………………………………
- bigAstroObject.A499 = 549329194.3128M;
- bigAstroObject.A500 = 799448442.2474M;
- Parallel.For(0, 200_000_000, (int x) => DoSomeThing(bigAstroObject));
- }
-
- private static int DoSomeThing(BigAstroObject bigAstroObject)
- {
-
- return 0;
- }
- }
- struct BigAstroObject
- {
- public decimal A001 { get; set; }
- public decimal A002 { get; set; }
- ……………………………………………………………………………………
- public decimal A499 { get; set; }
- public decimal A500 { get; set; }
- }
- }
Refer to the attachment “ParallelForLoopWithoutInParameter.cs” for complete code of the above code snippet.
Parallel For Loop With In Parameter
Let’s have a look at the below code snippet which is using “Parallel.For()” and passing parameter using in (passing a read-only reference of same copy rather than creating a new copy each time).
- namespace INPerformance
- {
- class Program
- {
- static void Main(string[] args)
- {
- BigAstroObject bigAstroObject = new BigAstroObject();
- bigAstroObject.A001 = 656371600.3042M;
- bigAstroObject.A002 = 876120136.2188M;
- ……………………………………………………………………………………………
- bigAstroObject.A499 = 549329194.3128M;
- bigAstroObject.A500 = 799448442.2474M;
- Parallel.For(0, 200_000_000, (int x) => DoSomeThing(bigAstroObject));
- }
-
- private static int DoSomeThing(in BigAstroObject bigAstroObject)
- {
-
- return 0;
- }
- }
- struct BigAstroObject
- {
- public decimal A001 { get; set; }
- public decimal A002 { get; set; }
- ……………………………………………………………………………………
- public decimal A499 { get; set; }
- public decimal A500 { get; set; }
- }
- }
Refer to the attachment “ParallelForLoopWithInParameter.cs” for complete code of the above code snippet.
If you would like to explore more about 7.x features, then you can go through following articles.
- C# 7.2 New Features with Visual Studio 2017
- C# 7.0 And C# 7.1 New Features - Part Two
- Top 10 New Features of C# 7 With Visual Studio 2017
- Visual Studio 15 Preview First Look & C# 7
- Understanding ref and out With C# 7