Introduction
Brief history
C#, pronounced as “see-sharp” (Kovacs, 2007), is a multi-paradigm programing language that was developed by Microsoft around year 2000 under, and is still under, the lead of the prominent Danish software engineer – Anders Hejlsberg (Microsoft, 2006).
The design of the language is heavily influenced by C++
and Java
(Naugler, 2007) during its infancy but had embarked on an increasingly divergent journey since then.
Throughout the almost 18 years of development, C# has gone through many major versions (Microsoft, 2017) – currently sitting at version 7.3, with version 8.0 at the bleeding edge of prerelease.
Why C# is invented?
The most plausible motivation for Microsoft creating a new language that is so similar to Java back then probably rooted in the “Sun Microsystems v. Microsoft Corporation” lawsuit (Shankland, 2002).
As the settlement of the case, Microsoft was forced to discontinue its proprietary Microsoft Java Virtual Machine (MSJVM) – an attempt of the giant to drive more customers to use Windows and Microsoft products (under the internally-used “Embrace, extend, and extinguish” slogan (Economist, 2000)).
Eventually, C# was born, “which was not intended to needle Sun” but “with that as a side effect” to Sun (Wong, 2000).
Side note
The “sharp” in C# should be the sharp symbol “♯” – however the hash symbol “#” is chosen as the close approximate because the sharp symbol is not present on most keyboard layouts (MSDN, 2006).
Besides from being inspired by musical notation (Kovacs, 2007), the name of C# can also be viewed as a ligature of four plus symbols arranged in a two-by-two grid, implying its close relation to both C and C++ as a form of “increment” to the former two (Hamilton, 2008).
References
- Kovacs, J. (2007, September 7). C#/.NET History Lesson - James Kovacs’ Weblog. Retrieved from http://jameskovacs.com/2007/09/07/cnet-history-lesson/
- Microsoft. (2006, October 3). (Archived) Anders Hejlsberg: Technical Fellow. Retrieved from https://web.archive.org/web/20090427201423/http://www.microsoft.com/presspass/exec/techfellow/Hejlsberg/default.mspx
- Naugler, D. R. (2007). C# 2.0 FOR C++ AND JAVA PROGRAMMER - CONFERENCE WORKSHOP. Journal of Computing Sciences in Colleges, 22(5).
- Microsoft. (2017, September 20). The history of C#. Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history
- Shankland, S. (2002, March 15). Sun, Microsoft settle Java suit. Retrieved from https://www.cnet.com/news/sun-microsoft-settle-java-suit/
- Economist, T. (2000, March 30). Deadly embrace. Retrieved from https://www.economist.com/business/2000/03/30/deadly-embrace
- Wong, W. (2000, June 26). (Archived) Microsoft aims at Sun’s Java with standards push. Retrieved from http://web.archive.org/web/20070713045941/http://news.com.com/2100-1001-242400.html
- MSDN. (2006). (Archived) Frequently Asked Questions About Visual C# .NET 2002. Retrieved from https://web.archive.org/web/20060214002638/http://msdn.microsoft.com/vcsharp/previous/2002/FAQ/default.aspx
- Hamilton, N. (2008, October 3). The A-Z of Programming Languages: C#. Retrieved from https://www.computerworld.com.au/article/261958/a-z_programming_languages_c_/?pp=2
Introduction
Supported programming styles
C# supports more than one programming style:
- Imperative programming
- Functional programming
etc..
Such language is better known as a “multi-paradigm” programming language.
Procedural programming
Procedural language describes the sequence of statement step by step to reach the goal (Hope, 2017). It tells the computer how to accomplish a task by breaking down a problem into multiple, smaller sub tasks. Here is an example function for calculating the total amount of payment adapted from (Lilly, 2016).
public void CalculateTotalAmount()
{
// Get all the orders
InventoryItem book =
new InventoryItem(1, "Learn C# in 30 Minutes", InventoryItem.SalesTaxCategory.Normal, (decimal)19.95,
(decimal)2.5);
InventoryItem ebook =
new InventoryItem(2, "Learn F# in 31 Minutes", InventoryItem.SalesTaxCategory.Normal, (decimal)9.95, 0);
InventoryItem energyDrink =
new InventoryItem(3, "Monster Energy Drink", InventoryItem.SalesTaxCategory.Food, (decimal)1.95,
(decimal)0.75);
// Get customer
Customer joe = new Customer(1, "Joe Smith", false);
// Create an order
Order order = new Order(1, joe);
order.LineItems.Add(new LineItem(book, 2));
order.LineItems.Add(new LineItem(ebook, 1));
order.LineItems.Add(new LineItem(energyDrink, 10));
// Rates
const decimal normalTaxRate = (decimal)0.08;
const decimal medicineTaxRate = (decimal)0.00;
const decimal foodTaxRate = (decimal)0.04;
const decimal luxuryTaxRate = (decimal)0.14;
decimal subTotal = (decimal)0.0;
decimal tax = (decimal)0.0;
decimal shippingFee = (decimal)0.0;
// Calculate sub total based on category
foreach(LineItem lineItem in order.LineItems)
{
decimal lineItemSubTotal = lineItem.Item.Price * lineItem.Quantity;
subTotal += lineItemSubTotal;
if(lineItem.Item.TaxCategory == InventoryItem.SalesTaxCategory.Medicine)
{
tax += lineItemSubTotal * medicineTaxRate;
}
else if(lineItem.Item.TaxCategory == InventoryItem.SalesTaxCategory.Food)
{
tax += lineItemSubTotal * foodTaxRate;
}
else if(lineItem.Item.TaxCategory == InventoryItem.SalesTaxCategory.Luxury)
{
tax += lineItemSubTotal * luxuryTaxRate;
}
else // Normal
{
tax += lineItemSubTotal * normalTaxRate;
}
if(!order.Customer.IsPremiumMember)
{
// Non-Premium customers pay 50 cents per pound for shipping.
shippingFee += lineItem.Item.Weight * lineItem.Quantity * (decimal)0.50;
}
}
// Calculate total
decimal total = subTotal + tax + shippingFee;
System.Console.WriteLine(subTotal);
System.Console.WriteLine(tax);
System.Console.WriteLine(shippingFee);
System.Console.WriteLine(total);
}
Object-oriented programming
C# also supports object-oriented programming, where a problem domain is model using “objects” which may contain data and behaviors.
The same example, but the business logic is moved to the Order
class.
public void CalculateTotalAmount()
{
InventoryItem book =
new InventoryItem(1, "Learn C# in 30 Minutes", InventoryItem.SalesTaxCategory.Normal, (decimal)19.95,
(decimal)2.5);
InventoryItem ebook =
new InventoryItem(2, "Learn F# in 31 Minutes", InventoryItem.SalesTaxCategory.Normal, (decimal)9.95, 0);
InventoryItem energyDrink =
new InventoryItem(3, "Monster Energy Drink", InventoryItem.SalesTaxCategory.Food, (decimal)1.95,
(decimal)0.75);
Customer joe = new Customer(1, "Joe Smith", false);
// Create an order
Order order = new Order(1, joe);
order.LineItems.Add(new LineItem(book, 2));
order.LineItems.Add(new LineItem(ebook, 1));
order.LineItems.Add(new LineItem(energyDrink, 10));
// Check values.
// Notice that nothing needs to be calculated in this code.
// All values are calculated in the business classes.
System.Console.WriteLine(order.SubTotal);
System.Console.WriteLine(order.Tax);
System.Console.WriteLine(order.ShippingFee);
System.Console.WriteLine(order.Total);
}
Functional programming
Functional language is a programming language built over and around logical functions or procedures within its programming structure. For example, a LINQ query:
database.Where(item => item.Customer.Name == "Joe")
.Select(item => item.CalculatePrice())
.Aggregate(0, (accumulate, price) => accumulate + price);
References
- Hope, C. (2017, December 7). Imperative programming. Retrieved from https://www.computerhope.com/jargon/i/imp-programming.htm
- Lilly, S. (2016, July 4). [C# Anti-Pattern] Procedural code in Object-Oriented code. Retrieved from https://scottlilly.com/c-anti-pattern-procedural-code-in-object-oriented-code/
Introduction
Unique features
There are some unique features of C# that make itself stand out from the sheer amount of programming languages in the wild.
Support for
- Property syntax
- Partial classes and methods
- Extension methods
- Anonymous types
- Language Integrated Query (LINQ)
are some of the prominent ones.
Property syntax
Property syntax allow classes to provide a flexible mechanism for getting, setting, or computing the value of a private field (Docs, 2017).
public class SaleItem
{
public string Name { get; set; }
public decimal Price { get; set; }
}
Partial classes and methods
Partial classes and methods allow a class’s members to be defined in multiple files (Docs, 2015) which provide the following benefits:
- multiple programmers to work on the same class at the same time
- provide a mechanism to extend a compiler generated code
// File1
public partial class Employee
{
public void DoWork()
{
}
// partial method
partial void onNameChanged();
}
// File2
public partial class Employee
{
public void GoToLunch()
{
}
partial void onNameChanged()
{
// method body
}
}
Extension methods
Extension method is a statically-defined method that also allows programmers to extend the functionality of a class that cannot be modified, such as the built-in types.
public static class StringExtension
{
// extend the built in string type
public static int WordCount(this string str)
{
return str.Split(new char[] {' ', '.','?'}, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
Anonymous types
Anonymous type provide a simpler way to encapsulate a set of read only property into a single object and it is not necessary to define a type first. The type name is generated by compiler (Docs, 2015).
var v = new { Amount = 108, Message = "Hello" };
Console.WriteLine(v.Amount + v.Message);
Language Integrated Query (LINQ)
Language Integrated Query (LINQ) provide a unified language for querying data from various data source including XML documents, SQL databases, ADO.NET datasets, .NET collections or any other format provided that a LINQ provider is available (Docs, 2015)
// Query syntax
var result = from student in students
where student.Scores[0] > 90
select student;
// Fluent syntax
var result = students.Where(student => student.Scores[0] > 90);
References
- Docs, M. (2017, March 9). Properties (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
- Docs, M. (2015, July 19). Partial Classes and Methods (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods
- Docs, M. (2015, July 19). Anonymous Types (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types
- Docs, M. (2015, July 19). Introduction to LINQ Queries (C#). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/introduction-to-linq-queries
Application Domain
Initial intended usage
As stated in the first edition of C# Language Specification (EMCA-334):
C# is intended to be a simple, modern, general-purpose, object-oriented programming language.
C# is intended to be suitable for writing applications for both hosted and embedded systems, ranging from the very large that use sophisticated operating systems, down to the very small having dedicated functions.
Thus it is apparent that C# is designed to be used in literally any application domain (if performance consideration is not taken into account).
Albeit its versatility, C# has mainly been used for developing commercial applications including:
- business applications
- web applications
In fact, almost the entire .NET framework is written in C# (jasonall, 2008)
References
- International, E. (2001). C# Language Specification (1st ed.). Retrieved from https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-334%201st%20edition%20December%202001.pdf
- jasonall. (2008, May 25). A Brief History Of C# Style. Retrieved from https://blogs.msdn.microsoft.com/sourceanalysis/2008/05/25/a-brief-history-of-c-style/
Application Domain
Current usage
Currently, C# has a widespread use in
- desktop applications, using various technologies like
- web sites, web applications and web services, primarily using ASP.NET
WinForms
WinForms, or Windows Forms, is graphical class library included as a part of Microsoft .NET Framework. Its popularity can be seen though the sheer amount of commercially-available custom user interface controls:
- DevExpress WinForms
- Windows Forms UI Controls by Infragistics
- Syncfusion Essential Studio for Windows Forms
- Xceed for WinForms
- Telerik UI for WinForms
- … and lots more
However, WinForms is now in maintenance mode as no new features will be added (Allen, 2014). Developers are also advised to move forward to its successor – the XAML based UI frameworks, like WPF and UWP.
One thing to note is that even if WinForms is nearly “dead”, there are still a lot of legacy WinForms applications in production till this date (Corey, 2018; Heath, 2014).
WPF
Almost all of the newer .NET desktop applications are written using WPF, some of the notable examples are:
- Microsoft Visual Studio
- Expression Blend, for designing XAML-based interfaces
and a lot more, compiled from (StackOverflow, 2011):
- Autodesk AutoCAD
- Nero
- Lenovo ThinkVantage
- HP Total Care Advisor
- Intel Rapid Storage Technology
- AMD Fusion Media Explorer
- Nokia Music
- HP TouchSmart
- …
ASP.NET
ASP.NET is an open-source (Microsoft, 2017) framework for building modern web apps and services with .NET. According to BuiltWith (BuiltWith, 2018), there are more than 40 million websites using ASP.NET.
Some of the prominent examples are:
- StackOverflow (Craver, 2013)
- Bing, and other Microsoft websites
- namecheap
- W3Schools
- Dell
References
- Allen, J. (2014, April 3). A WPF Q&A. Retrieved from https://www.infoq.com/news/2014/04/WPF-QA
- Corey, T. (2018, April 13). IS WINFORMS DEAD? Retrieved from https://iamtimcorey.com/ask-tim-is-winforms-dead/
- Heath, M. (2014, June 5). Is Windows Forms Dead Yet? Retrieved from http://mark-dot-net.blogspot.com/2014/06/is-windows-forms-dead-yet.html
- StackOverflow. (2011, June 5). What real world WPF applications are out there? Retrieved from https://stackoverflow.com/a/4456279
- Microsoft. (2017, July 25). SP.NET MVC, Web API, and Web Pages (Razor) are Open Source Projects. Retrieved from https://www.asp.net/open-source
- BuiltWith. (2018). Websites using ASP.NET. Retrieved from https://trends.builtwith.com/websitelist/ASP.NET
- Craver, N. (2013, November 22). What it takes to run Stack Overflow. Retrieved from https://nickcraver.com/blog/2013/11/22/what-it-takes-to-run-stack-overflow/
Application Domain
Popularity
Yes, C# language currently is a popular language.
The way to measure the popularity of a programming language is by examining the job demand because it reflects what the industries are searching for. Below shows the most in-demand languages of the end of the year 2017 (Putano, 2017).
As we can see that C# is the 3rd most popular languages from the job websites, signifying its weight in Line of Business applications.
According to Glassdoor, the average C# developer salary, as of April 2018 in the United States, was $79,954. The range extended from $61,000 to a high of $104,000 per annum (Glassdoor, 2018).
Beside from the career prospects and the salary expectations, we can also analyze the number of users of using each language programming languages from GitHub. Here, GitHub is the most suitable choice of data sources because as of June 2018, GitHub reported to have over 30 million users (GitHub, 2018) and 57 million repositories (Firestine, 2017), making it one of the largest host of source code in the world (Gousios, Vasilescu, Serebrenik, & Zaidman, 2018).
Below shows the overall rankings for each language (Frederickson, 2018), we can see that C# is the 7th most used programming language.
Why so many people are using C#?
- the mature ecosystem surrounding C#, including
- the greatest IDE of all: Microsoft Visual Studio, which also offers
- a free version - Microsoft Visual Studio Community
- a Mac version - Microsoft Visual Studio for Mac
- and even a lightweight, cross-platform version - Visual Studio Code
- .NET Framework, and the vast libraries offered by the framework
- .NET Core, the open source and cross platform version of .NET Framework
- the greatest IDE of all: Microsoft Visual Studio, which also offers
- C# has a small learning curve (Mashable, 2018)
- C# is very versatile for developing a multitude of different applications, including
- Desktop apps
- WinForms
- WPF
- UWP
- Web servers and clients
- ASP.NET
- ASP.NET Core
- Mobile apps
- Xamarin
- Games
- Unity
- Desktop apps
- the language itself still evolving continuously, which gained even more traction with the open source of .NET
Where C# is used?
C# is used in mainly business domain including desktop and web application as stated before (Current Usage of C#). Recently, C# is also getting more and more attention in the following areas:
- Game development
- Mobile development
- Cross-platform development
- Artificial Intelligence
References
- Putano, B. (2017, December 18). Most Popular and Influential Programming Languages of 2018. Retrieved from https://stackify.com/popular-programming-languages-2018/
- Glassdoor. (2018, July 15). C#.net Developer Salaries (Glassdoor). Retrieved from https://www.glassdoor.com/Salaries/c-net-developer-salary-SRCH_KO0,15.htm
- GitHub. (2018, July 27). User Search. Retrieved from https://github.com/search?q=type:user&type=Users
- Firestine, B. (2017, April 10). Celebrating nine years of GitHub with an anniversary sale. Retrieved from https://blog.github.com/2017-04-10-celebrating-nine-years-of-github-with-an-anniversary-sale/
- Gousios, G., Vasilescu, B., Serebrenik, A., & Zaidman, A. (2018). Lean GHTorrent: GitHub Data on Demand. Retrieved from https://bvasiles.github.io/papers/lean-ghtorrent.pdf
- Frederickson, B. (2018, January 25). Ranking Programming Languages by GitHub Users. Retrieved from https://www.benfrederickson.com/ranking-programming-languages-by-github-users/
- Mashable. (2018, March 17). It pays to learn to code with C# and here’s why. Retrieved from https://mashable.com/2018/03/17/coding-course-class-bootcamp/
Names and Bindings
Names
A name is a string of characters used to identify some entity in a program. C# imposes no limit on the length of names and all characters are significant (meaning that they are case sensitive).
However, identifiers in C# must obey the following rules (Jones, 2001)
- starts with either
- letters, or
- underscore character
- contains only
- letters
- digits
- underscore character
- must not be a keyword or a reserved word (which can be “escaped” by prefixing with the verbatim character @)
References
- Jones, B. L. (2001, December 14). Storing Information with Variables in C#. Retrieved from http://www.informit.com/articles/article.aspx?p=24472
Names and Bindings
Special characters
Special characters are used to modify the program element which they prefixed. C# supports the following special characters (Docs, 2017):
@, the verbatim identifier character
The verbatim character can be used to
1. enable keywords to be used as identifiers
This is illegal in C#:
string string = "this is a string";
But this is a valid syntax:
string @string = "this is a string";
Console.WriteLine(@string);
Live-code example
2. indicate that a string is a “verbatim string literal”
1.1 escape sequences are ignored
string normal = "first line \n second line";
string verbatim = @"first line \n second line";
Console.WriteLine(normal);
// --- output:
// first line
// second line
Console.WriteLine(verbatim);
// --- output:
// first line \n second line
which is commonly used for specifying file paths in Windows
// need to use '\' to escape the backslash character
string filename1 = "c:\\documents\\files\\u0066.txt";
// ignore escape sequences
string filename2 = @"c:\documents\files\u0066.txt";
Console.WriteLine(filename1);
// --- output:
// c:\documents\files\u0066.txt
Console.WriteLine(filename2);
// --- output:
// c:\documents\files\u0066.txt
Live-code example
1.2 can be used to define multiline string
string normal = "line1\nline2\nline3\n---";
string verbatim = @"line1
line2
line3
---";
Console.WriteLine(normal);
Console.WriteLine(verbatim);
Output:
line1
line2
line3
---
line1
line2
line3
---
Live-code example
$, the interpolated string character
Provide a more readable and convenient syntax to format strings (Docs, 2018).
Using this code snippet as an example:
double price = 12.456;
string name = "Lorem Ipsum";
DateTime paidOn = new DateTime(2018, 1, 1, 12, 34, 56);
Instead of doing this to join different variables together
// instead of doing this
string normal = name + " paid " + price.ToString("0.00") + " on " + paidOn.ToString("yyyy-MM-dd HH:mm:ss");
// Note: 00.00 -> numbers are formatted into 2 decimal places, padded with zeros
String interpolation is much simpler
// you can do this
string interpolated = $"{name} paid {price:0.00} on {paidOn:yyyy-MM-dd HH:mm:ss}";
Console.WriteLine(normal);
Console.WriteLine(interpolated);
Output:
Lorem Ipsum paid 12.46 on 2018-01-01 12:34:56
Lorem Ipsum paid 12.46 on 2018-01-01 12:34:56
Live-code example
References
- Docs, M. (2017, February 14). C# Special Characters. Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/
- Docs, M. (Ed.). (2018, March 26). string interpolation (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
Names and Bindings
Special words
Reserved words
Reserved words are names or identifiers that have predefined meanings to the compiler. They cannot be used as a user-defined identifiers unless they are prefixed with the verbatim character @.
Below are all the reserved words in C# (Docs, 2017).
abstract | as | base | bool |
break | byte | case | catch |
char | checked | class | const |
continue | decimal | default | delegate |
do | double | else | enum |
event | explicit | extern | false |
finally | fixed | float | for |
foreach | goto | if | implicit |
in | int | interface | internal |
is | lock | long | namespace |
new | null | object | operator |
out | override | params | private |
protected | public | readonly | ref |
return | sbyte | sealed | short |
sizeof | stackalloc | static | string |
struct | switch | this | throw |
true | try | typeof | uint |
ulong | unchecked | unsafe | ushort |
using | using static | virtual | void |
volatile | while |
Contextual keywords
They only have special meanings under certain contexts (Docs, 2017).
add | alias | ascending |
async | await | descending |
dynamic | from | get |
global | group | into |
join | let | nameof |
orderby | partial (type) | partial (method) |
remove | select | set |
value | var | when (filter condition) |
where (generic type constraint) | where (query clause) | yield |
References
- Docs, M. (2017, March 7). C# Keywords. Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
Names and Bindings
Declarations
Explicit declarations
Explicit declaration is where the type of the variables is specified during declaration.
int age = 23;
string name = "Lorem Ipsum";
double money = 123.45;
Console.WriteLine($"Type of age: {age.GetType()}");
Console.WriteLine($"Type of name: {name.GetType()}");
Console.WriteLine($"Type of money: {money.GetType()}");
Output:
Type of age: System.Int32
Type of name: System.String
Type of money: System.Double
Live-code example
Implicit declarations
C# compiler uses type inferencing to deduce the actual type of the variable declared with the keyword var
. Note that (Docs, 2015)
var
keyword can only be used for variables declared at method scope- an implicitly declared variable is strongly typed (just as if you had declared the type yourself), which is different from the
dynamic
type.
var age = 23;
var name = "Lorem Ipsum";
var money = 123.45;
Console.WriteLine($"Type of age: {age.GetType()}");
Console.WriteLine($"Type of name: {name.GetType()}");
Console.WriteLine($"Type of money: {money.GetType()}");
Output:
Type of age: System.Int32
Type of name: System.String
Type of money: System.Double
This is not allowed since age
is statically-typed (as int
)
// ERROR: Cannot implicitly convert type "string" to "int"
age = "123";
Implicit declaration is extremely useful for creating anonymous classes, which are used extensively in LINQ operation.
var anony = new {
Name = "Lorem Ipsum",
Age = 23,
Money = 123.45
};
Console.WriteLine($"Type: {anony.GetType()}");
Console.WriteLine($"Name: {anony.Name} Age: {anony.Age} Money: {anony.Money}");
Output:
Type: <> f__AnonymousType0`3[System.String, System.Int32, System.Double] <-- this will be different
Name: Lorem Ipsum Age: 23 Money: 123.45
References
- Docs, M. (Ed.). (2015, July 20). var (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/var
Names and Bindings
Dynamic type binding
Variables with the type of dynamic
will bypass static type checking, errors will only be caught on runtime (Docs, 2015).
dynamic number = 123;
number = number + 456;
Console.WriteLine($"Value: {number}\tType: {number.GetType()}");
Output:
Value: 579 Type: System.Int32
We are allowed to assign a string to the same variable (which is initially an int
).
number = "a string";
Console.WriteLine($"Value: {number}\tType: {number.GetType()}");
Output:
Value: a string Type: System.String
Because the variable is now a string, subtracting an int
will result in a runtime error
number = number - 1;
// ERROR: Operator '-' cannot be applied to operands of type 'string' and 'int'
Live-code example
References
- Docs, M. (Ed.). (2015, July 20). dynamic (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/dynamic
Names and Bindings
Storage Binding
Storage binding defines when a variable is bound to a memory cell and for how long it remains bounded. C# only has 3 binding categories:
Static
In C#, local variables cannot be defined as static
.
void Main()
{
// ERROR: The modifier "static" is not valid for this item
static int age = 123;
}
Instead, the static
keyword has another meaning: it is used to declare a static member, which belongs to the type itself rather than to a specific instance of the class (Docs, 2015).
public class Person
{
// static class member
public static int Age = 123;
}
void Main()
{
Console.WriteLine($"Age: {Person.Age}");
}
Output:
Age: 123
It can only be used on:
- classes
- fields
- methods
- properties
- operators
- events
- constructors
Stack-dynamic
In C#, value types are often but, not necessarily always, allocated on the stack. This includes (Cochran, 2006):
- bool
- byte
- char
- decimal
- double
- enum
- float
- int
- long
- sbyte
- short
- struct
- uint
- ulong
- ushort
Explicit heap-dynamic
Reference types are always allocated on the heap, which includes:
- class
- interface
- delegate
- object
- string
The heap is managed by Garbage Collector (GC), which will find all objects in the heap that are not being accessed and delete them. Note that a value type will be allocated on the heap when (Gabe, 2010):
- it is part of a class
- it is boxed
- it is an array
- it is a static variable
- it is capture by a closure
- … etc
Example
Adapted from (Cochran, 2006)
public class MyInt
{
public int MyValue;
}
void Main()
{
// on stack
int value = 123;
// on heap, but the reference/pointer to the object is on the stack
MyInt result = new MyInt();
// on heap, because MyValue is part of a class (which is a reference type)
result.MyValue = value + 200;
}
When the variables go out of scope, they are popped from the stack.
The object on the heap will eventually be collected by Garbage Collection.
References
- Docs, M. (Ed.). (2015, July 20). static (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static
- Cochran, M. (2006, January 14). C# Heap(ing) Vs Stack(ing) in .NET: Part I. Retrieved from https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net-part-i/
- Gabe. (2010, December 20). Memory allocation: Stack vs Heap? Retrieved from https://stackoverflow.com/a/4487320
Names and Bindings
Scoping
C# only support static scoping (also known as lexical scoping), where the scope of a variable is determined when the code is compiled (Rouse, 2011).
public class Program
{
int num;
static void Func1()
{
int num = 123;
}
public static void Main()
{
int num = 567;
Func1();
Console.WriteLine($"num1: {num}");
}
}
The output is
num1: 567
instead of 123
as defined in Func1()
(if C# uses dynamic scoping).
Live-code exmaple
References
- Rouse, M. (2011, March 1). lexical scoping (static scoping). Retrieved from https://whatis.techtarget.com/definition/lexical-scoping-static-scoping
Expressions and Assignments
Expressions
An expression is a sequence of
- one or more operands, and
- zero or more operators
that can be evaluated into some value (Docs, 2017).
In C#, expressions can consist of
- literal value
1
,“abc”
- method invocation
CallMe()
- operator and operands
1 + 2 - 3
- simple name
- variable:
num
,age
- method parameter:
CallMe(myNumber)
- variable:
There are also other types of expressions, for example:
- LINQ query expressions (Docs, 2016)
IEnumerable<int> scoreQuery = from score in scores where score > 80 select score;
- lambda expressions,
(int x, string s) => s.Length > x
References
- Docs, M. (2017, May 11). Expressions (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expressions
- Docs, M. (2016, November 30). Language Integrated Query (LINQ). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/linq/index
Expressions and Assignments
Arithmetic expressions
An arithmetic expression is an expression built up using
- numbers
- arithmetic operators (such as
+
,-
,*
,/
), and - parentheses
Arithmetic operators
In C#, the primary arithmetic operators are:
Operators | Description |
---|---|
- (unary) | Negation |
* | Multiplication |
/ | Division |
+ | Addition |
- | Subtraction |
% | Modulo |
Here is a simple example demonstrating some of the operators:
Console.WriteLine(1 + 2); // 3
Console.WriteLine(1 - -2); // 3
Console.WriteLine(1 * 2); // 2
Console.WriteLine(1 / 2); // 0
Console.WriteLine(1 / 2.0); // 0.5
Console.WriteLine(5 % 3); // 2
Live-code example
Mixed mode arithmetic operations
In C#, an arithmetic expression can contain operands of different integral type. However, the resulting type will often be the larger integral type (widening assignment coercions). For example,
int a = 1;
double b = 1.2;
var result = a + b;
Console.WriteLine(result.GetType());
The output will be
System.Double
Live-code example
References
- Wagner, E. G. (2001, October 23). Arithmetic Expressions. Retrieved from https://www.ii.uib.no/ wagner/hsalg/arithexp.htm
- Techotopia. (2016, October 27). C Sharp Operators and Expressions. Retrieved from https://www.techotopia.com/index.php/C_Sharp_Operators_and_Expressions
Expressions and Assignments
Operators
Types of operator
Unary operators
Operators that take 1 operand. For example,
- increment operator,
++
- new operator,
new Friend();
- … etc
Binary operators
Operators that take 2 operands. For example,
- arithmetic operators
- addition,
+
- subtraction,
-
- multiplication,
*
- division,
/
- modulo,
%
- addition,
- logical operators
- Logical AND,
x & y
- Logical XOR,
x ^ y
- Logical OR,
x | y
- Conditional AND,
x && y
- Conditional OR,
x || y
- Logical AND,
- .. etc
Ternary operators
Operators that take 3 operands. The only ternary operator in C# is the conditional operator, ?:
(Docs, 2015)
For example, this if-else
statement
if (input > 0)
{
classify = "positive";
}
else
{
classify = "negative";
}
can be written as
classify = (input > 0) ? "positive" : "negative";
References
- Docs, M. (2015, July 20). Operators (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/operators
- Docs, M. (2015, July 20). ?: Operator (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator
Expressions and Assignments
Operator precedence and associativity
An expression can contain multiple operators – in that case, when two operators share an operand, the order in which the operators are applied is determined by
- operator precedence
- associativity, and
- parentheses
For example,
3 + 4 * 5
is treated as
3 + (4 * 5)
which evaluates to 23
because multiplication *
has higher precedence over addition +
.
Here is a list of almost all operators in C#, sorted in their order of precedence.
Category | Operator | Associativity |
---|---|---|
Postfix | () [] -> . ++ – | Left to right |
Unary | + - ! ~ ++ – (type)* & sizeof | Right to left |
Multiplicative | * / % | Left to right |
Additive | + - | Left to right |
Shift | « » | Left to right |
Relational | < <= > >= | Left to right |
Equality | == != | Left to right |
Bitwise AND | & | Left to right |
Bitwise XOR | ^ | Left to right |
Bitwise OR | | | Left to right |
Logical AND | && | Left to right |
Logical OR | || | Left to right |
Conditional | ?: | Right to left |
Assignment | = += -= *= /= %=»= «= &= ^= |= | Right to left |
Comma | , | Left to right |
Parenthesis
The implied evaluation order can be changed by using parenthesis. For example,
(3 + 4) * 5
results in 35
because the addition operator is evaluated first, even though the multiplication operator has higher precedence
References
- Docs, M. (2015, July 20). ?: Operator (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator
Expressions and Assignments
Operator overloading
In C#, user-defined types can define custom behaviors for certain operators with static member functions using the operator
keyword. However, not all operators can be overloaded, and some of them have certain restrictions (Docs, 2015)
Operators | Description |
---|---|
+, -, !, ~, ++, – | Can be overloaded |
+, -, *, /, % | Can be overloaded |
==, !=, , <=, >= | Can be overloaded |
&&, || | Cannot be overloaded directly |
+=, -=, *=, /=, %= | Cannot be overloaded directly |
=, ., ?:, ->, new, is, sizeof, typeof | Cannot be overloaded |
Example: Fraction
public class Fraction
{
private int _numerator;
private int _denominator;
public Fraction(int numerator, int denominator)
{
_numerator = numerator;
_denominator = denominator;
}
public double Value => ((double)_numerator) / _denominator;
public static Fraction operator +(Fraction first, Fraction second)
{
int numerator = first._numerator * second._denominator + second._numerator * first._denominator;
int denominator = first._denominator * second._denominator;
return new Fraction(numerator, denominator);
}
}
To see the class in action,
Fraction f1 = new Fraction(2, 3);
Fraction f2 = new Fraction(3, 5);
Fraction result = f1 + f2; // adding 2 fractions together
Console.WriteLine(result.Value);
Output:
1.26666666666667
which is equivalent to 2 / 3 + 3 / 5
.
The method
public static Fraction operator +(Fraction first, Fraction second)
overload the operator +
to provide custom logic for adding two fractions.
Live-code example
References
- Docs, M. (2015, July 20). Overloadable Operators (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators
Expressions and Assignments
Functional side effects
Functional side effects occur when functions
- modify a global variable, or
- modify one of its parameters
Modifying one of its parameters
Mutations made by the function will persist only if the parameters are passed by reference, which can be achieved using either the keyword ref
or out
.
For example,
int fun1(ref int num)
{
num = 100;
return 10;
}
void main()
{
int a = 2;
a = fun1(ref a) + a;
Console.WriteLine(a);
}
The output will be 110
However, the variable will not be modified if there is no ref
or out
keyword:
int fun1(int num)
{
num = 100;
return 10;
}
void main()
{
int a = 2;
a = fun1(a) + a;
Console.WriteLine(a);
}
The result will be 12
The difference between ref
and out
is that ref
parameter must be initialized before it is passed; while out
parameter do not have to be explicitly initialized before they are passed (Docs, 2018).
References
- Docs, M. (2018, March 6). ref (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref
Expressions and Assignments
Type conversion
Type conversion is a process where data of one type is converted into data of another type (Docs, 2015). C# has 2 forms of type conversions:
- implicit conversions
- explicit conversions (or type casting)
Implicit conversions
The conversions are performed by C# in a type-safe manner, most of which are widening conversion.
For example, conversions
from smaller to larger integral types
int num1 = 10;
double num2 = num1;
from derived classes to base classes
Derived d = new Derived();
Base b = d;
Explicit type conversion
Casting is required when information might be lost in the conversion, which is also known as a narrowing conversion. Explicit conversions require a cast operator in the form of (<target_type>)<data>
.
For example, conversions
from larger to smaller integral types
double num1 = 10;
// int num2 = num1; // error
int num2 = (int)num1;
from base class to child class
Derived d = new Derived();
Base b = d;
// Derived d2 = b; // error
Derived d2 = (Derived)b;
References
- Docs, M. (2015, July 20). Casting and Type Conversions (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions
Expressions and Assignments
Boolean expressions
Boolean expressions are those that always evaluate to either true
or false
. Here are some examples of operators that usually can be found within a boolean expression:
- relational and equality operators
- logical negation,
!
- type operators
Relational expressions
A relational expression consists of two or more expressions whose values are compared to determine whether the relationship stated by the relational operator is satisfied (Schmalzl, 1999).
Example of relational and equality operators:
Expression | Description |
---|---|
x < y | Less than |
x > y | Greater than |
x <= y | Less than or equal |
x >= y | Greater than or equal |
x == y | Equal |
x != y | Not equal |
Console.WriteLine($"1 > 2 is {1 > 2}");
Console.WriteLine($"1 < 2 is {1 < 2}");
Console.WriteLine($"1 <= 1 is {1 <= 1}");
Console.WriteLine($"2 >= 2 is {2 >= 2}");
Console.WriteLine($"2 == 2 is {2 == 2}");
Console.WriteLine($"2 != 2 is {2 != 2}");
The output:
1 > 2 is False
1 < 2 is True
1 <= 1 is True
2 >= 2 is True
2 == 2 is True
2 != 2 is False
Live-code example
References
- Schmalzl, J. (1999, July 5). Relational Expressions. Retrieved from http://earth.uni-muenster.de/ joergs/doc/f90/lrm/lrm0064.htm
Expressions and Assignments
Short circuit evaluation
Short-circuit evaluation is a process whereby the value of an expression is determined without evaluating all of the operands and/or operators.
C# offers both short-circuiting and non-short-circuiting boolean operators (Shamseer, 2017):
- short-circuiting operators
- conditional OR,
||
- conditional AND,
&&
- conditional OR,
- non-short-circuiting operators
- logical OR,
|
- logical AND,
&
- logical OR,
Here is an example demonstrating the differences between them
bool Condition1()
{
Console.WriteLine("\tCondition 1 is evaluated");
return false;
}
bool Condition2()
{
Console.WriteLine("\tCondition 2 is evaluated");
return true;
}
Console.WriteLine("--- Conditional operators");
Console.WriteLine();
Console.WriteLine("Condition1() && Condition2()");
if (Condition1() && Condition2())
{
// only Condition 1 is evaluated
}
Console.WriteLine();
Console.WriteLine("Condition2() || Condition1()");
if (Condition2() || Condition1())
{
// only Condition 2 is evaluated
}
Console.WriteLine();
Console.WriteLine("Condition1() || Condition2()");
if (Condition1() || Condition2())
{
// both are evaluated
}
Console.WriteLine();
Console.WriteLine("--- Logical operators");
Console.WriteLine();
Console.WriteLine("Condition1() & Condition2()");
if (Condition1() & Condition2())
{
// only Condition 1 is evaluated
}
Console.WriteLine();
Console.WriteLine("Condition1() | Condition2()");
if (Condition1() | Condition2())
{
// both are evaluated
}
The output:
Condition1()
returnsfalse
, whileCondition2()
returnstrue
--- Conditional operators
Condition1() && Condition2()
Condition 1 is evaluated
Condition2() || Condition1()
Condition 2 is evaluated
Condition1() || Condition2()
Condition 1 is evaluated
Condition 2 is evaluated
--- Logical operators
Condition1() & Condition2()
Condition 1 is evaluated
Condition 2 is evaluated
Condition1() | Condition2()
Condition 1 is evaluated
Condition 2 is evaluated
Live-code example
References
- Shamseer. (2017, January 19). Short-Circuit Evaluation In C#. Retrieved from https://www.c-sharpcorner.com/article/short-circuit-evaluation-in-c-sharp/
Control Structures
Selection statements
Selection statements are used for transferring the program control to a specific flow based on the result of some conditional expressions (which return either true
or false
) (Docs, 2015).
In C# exists mainly 2 types of selection statement:
References
- Docs, M. (2015, July 20). Selection Statements (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/selection-statements
Control Structures
if statements
An if statement identifies which block code to run based on the value of a Boolean
expression (Docs, 2015)
bool condition = true;
if (condition)
{
Console.WriteLine("The variable is set to true.");
}
else
{
Console.WriteLine("The variable is set to false.");
}
Output:
The variable is set to true.
If the condition is set to false
:
bool condition = false;
if (condition)
{
Console.WriteLine("The variable is set to true.");
}
else
{
Console.WriteLine("The variable is set to false.");
}
Output:
The variable is set to false.
Live-code example
The else
part of the if-else
statement is optional:
bool condition = true;
if (condition)
{
Console.WriteLine("The variable is set to true.");
}
Console.WriteLine("This will always be printed.");
Output:
The variable is set to true.
This will always be printed.
Nested if-else
If-else statements can be nested as well in which multiple conditions can be tested.
bool condition1 = true;
bool condition2 = false;
if (condition1)
{
if (condition2)
{
Console.WriteLine("condition1 and condition2 is true");
}
else
{
Console.WriteLine("condition1 is true but condition2 is false");
}
}
else
{
Console.WriteLine("condition1 is false");
}
Live-code example
Here is another example that test whether the user input is a letter, then check whether it is uppercase or lowercase:
Console.Write("Enter a character: ");
char c = (char)Console.ReadLine()[0];
if (Char.IsLetter(c))
{
if (Char.IsLower(c))
{
Console.WriteLine("The character is lowercase.");
}
else
{
Console.WriteLine("The character is uppercase.");
}
}
else
{
Console.WriteLine("The character isn't an alphabetic character.");
}
Sample output (try it on the live-code example below!):
Enter a character: a
The character is lowercase.
Enter a character: A
The character is uppercase.
Enter a character: 1
The character isn't an alphabetic character.
Live-code example
else if
Other than nesting if-else
statements, the else if
clause can be used to test multiple condition. The same example above can be written as:
Console.Write("Enter a character: ");
char c = (char)Console.ReadLine()[0];
if (Char.IsLower(c))
{
Console.WriteLine("The character is lowercase.");
}
else if (Char.IsUpper(c))
{
Console.WriteLine("The character is uppercase.");
}
else if (Char.IsDigit(c))
{
Console.WriteLine("The character is a digit");
}
else
{
Console.WriteLine("The character isn't an alphabetic character.");
}
Sample output:
Enter a character: a
The character is lowercase.
Enter a character: B
The character is uppercase.
Enter a character: 1
The character is a digit
Enter a character: )
The character isn't an alphabetic character.
Live-code example
Logical operators
As we have covered earlier, we can use logical operators to make a compound condition.
NOT
// Logical NOT, !
bool result = true;
if (!result)
{
Console.WriteLine("result is not true");
}
else
{
Console.WriteLine("result is not false");
}
AND
// AND, &&
int m = 9;
int n = 7;
int p = 5;
if (m >= n && m >= p)
{
Console.WriteLine("Nothing is larger than m.");
}
OR
// OR, ||
if (m > n || m > p)
{
Console.WriteLine("m isn't the smallest.");
}
Combined
// AND and NOT
if (m >= n && !(p > m))
{
Console.WriteLine("Nothing is larger than m.");
}
// NOT and OR
m = 4;
if (!(m >= n || m >= p))
{
Console.WriteLine("Now m is the smallest.");
}
Live-code example
References
- Docs, M. (2015, July 20). if-else (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/if-else
Control Structures
switch statements
switch is a selection statement that chooses a single switch section to execute from a list of candidates based on a pattern match with the match expression (Docs, 2018)
int caseSwitch = 2;
switch (caseSwitch)
{
case 1:
Console.WriteLine("Case 1");
break;
case 2:
case 4:
Console.WriteLine($"Case {caseSwitch}");
break;
case 3:
Console.WriteLine("Case 3");
break;
default:
Console.WriteLine($"Unexpcted value ({caseSwitch})");
break;
}
Output:
Case 2
Live-code example
The general syntax for each case label is
case constant:
where constant can be any of the following expressions:
- A
bool
literaltrue
, orfalse
- Any integral constant
int
long
byte
- etc
- The name of a declared
const
variable. - An enumeration constant.
DayOfWeek.Sunday
- A
char
literal. - A
string
literal.
The switch statement provides a clean alternative for writing multiple if-else statements. The example above can be written as:
int caseSwitch = 1;
if (caseSwitch == 1)
{
Console.WriteLine("Case 1");
}
else if (caseSwitch == 2 || caseSwitch == 4)
{
Console.WriteLine($"Case {caseSwitch}");
}
else if (caseSwitch == 3)
{
Console.WriteLine("Case 3");
}
else
{
Console.WriteLine($"Unexpcted value ({caseSwitch})");
}
Live-code example
Multiple case labels
As you might have noticed in the previous switch example, a switch section can have multiple case labels
case 2:
case 4:
Console.WriteLine($"Case {caseSwitch}");
However in that case, C# does not allow execution to continue from one switch section to the next. The code below will generate a compiler error:
CS0163: “Control cannot fall through from one case label () to another.”
int caseSwitch = 1;
switch (caseSwitch)
{
// The following switch section causes an error.
case 1:
Console.WriteLine("Case 1...");
// a jump statement is needed here
case 2:
Console.WriteLine("... and/or Case 2");
break;
}
Live-code example
Jump statements include:
- break
- goto case
- goto label
- return
- throw
default case
The default
case, which is optional, specifies what to execute if the match expression does not match with any of the case labels. This is similar to the last else
clause in the if-elseif-else example.
References
- Docs, M. (Ed.). (2018, August 14). switch (C# reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch
Control Structures
Iteration statements
In C#, there are several constructs that can be used to repeat a block of code, including:
do
and while
are logically-controlled loop constructs, for
is counter-controller, and finally foreach…in
is used together for iteration based on data structures (specifically, those that implement IEnumerable<T>
)
User-located loop control mechanisms are achieved using jump statements, some of which include (Docs, 2015):
References
- Docs, M. (2015, July 20). Iteration Statements (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/iteration-statements
- Docs, M. (2015, July 20). Jump Statements (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/jump-statements
Control Structures
while loop
The structure of while
loop is as follow:
while (condition)
{
statements
}
The condition
is tested before the statements
in the body of the while loop is executed. Hence, while
loop executes zero or more times (Docs, 2018).
int x = 10;
while (x < 20)
{
Console.WriteLine($"Value of x: {x}");
x++;
}
Output:
Value of x: 10
Value of x: 11
Value of x: 12
Value of x: 13
Value of x: 14
Value of x: 15
Value of x: 16
Value of x: 17
Value of x: 18
Value of x: 19
Live-code example
References
- Docs, M. (2018, May 28). while (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/while
Control Structures
do...while loop
The structure of the do...while
loop is as follow:
do
{
statements
} while (condition);
As the condition
is evaluated after executing the statements
, do…while
loop is guaranteed to be executed at least once (Docs, 2018).
int x = 10;
do
{
Console.WriteLine($"Value of x: {x}");
x++;
} while (x < 20);
Output:
Value of x: 10
Value of x: 11
Value of x: 12
Value of x: 13
Value of x: 14
Value of x: 15
Value of x: 16
Value of x: 17
Value of x: 18
Value of x: 19
Live-code example
References
- Docs, M. (2018, May 28). do (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/do
Control Structures
for loop
The structure of a for
loop is as follow:
for (initializer; condition; iterator)
{
body
}
The body
of the for loop is executed when the condition
evaluates to true
.
A for
loop consists of main 3 parts:
- initializer
- condition
- iterator
initializer
The statements in the initializer section are executed first and executed only once before entering the loop.
This is usually where a local loop variable is declared and initialized.
condition
The condition section is a
Boolean
expression which is evaluated before every iteration.
The for loop will stop executing when it evaluates to false
.
iterator
The statements in the iterator section are executed after each iteration.
This is usually where the loop variable is incremented or decremented.
Example
The same example used in while
and do…while
loop can be written using for loop, which is more concise:
for (int x = 10; x < 20; x++)
{
Console.WriteLine($"Value of x: {x}");
}
Output:
Value of x: 10
Value of x: 11
Value of x: 12
Value of x: 13
Value of x: 14
Value of x: 15
Value of x: 16
Value of x: 17
Value of x: 18
Value of x: 19
In this case,
Initializer | Condition | Iterator |
---|---|---|
int x = 10 |
x < 20 |
x++ |
Live-code example
References
- Docs, M. (2018, June 13). for (C# reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/for
Control Structures
foreach...in loop
foreach
statement provide a simpler way to iterate over a collection of elements. It can be used with types that implement the System.Collections.IEnumerable
or System.Collections.Generic.IEnumerable<T>
interface. Examples of those types include:
- arrays
List<T>
Dictionary<TKey,TValue>
Queue<T>
SortedList<TKey,TValue>
Stack<T> Class
- … etc
The structure of a foreach…in
loop is as follow:
foreach (type name in collection)
{
body
}
where
type
is the type of the elements inside thecollection
,var
keyword can also be used herecollection
is the groups of elements to be iterated
For example, this for loop that print out all the elements in an array
int[] nums = {1, 2, 3, 45, 6, 7, 8, 9};
for (int i = 0; i < nums.Length; i++)
{
Console.WriteLine(nums[i]);
}
can be written using foreach…in
loop
int[] nums = {1, 2, 3, 45, 6, 7, 8, 9};
foreach (int num in nums)
{
Console.WriteLine(num);
}
Live-code example
References
- Docs, M. (2018, June 29). foreach, in (C# reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in‘
Control Structures
break
A break
statement will terminate
- the closest enclosing loop, or
- the switch statement
and return the program control to the statements right after the terminated statements (Docs, 2015).
For example
for (int i = 0; i <= 10; i++)
{
if (i == 5)
{
break;
}
Console.WriteLine(i);
}
will print:
0
1
2
3
4
because the for loop is terminated when i
is 5.
Live-code example
When break is used within a nested loop, it will only terminate the closest enclosing loop and return the control to the outer loop.
For example
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (j > i)
{
break;
}
Console.Write($"{j}");
}
Console.WriteLine();
}
Output:
0
01
012
0123
01234
012345
0123456
01234567
012345678
0123456789
Live-code example
As discussed earlier, break
statement is also used in switch
statements.
References
- Docs, M. (2015, July 20). break (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/break
Control Structures
continue
Different from break
, the continue
statement does not terminate the loop, instead it passes the control to the next iteration (Docs, 2015).
Using the previous example
for (int i = 0; i <= 10; i++)
{
if (i == 5)
{
continue;
}
Console.WriteLine(i);
}
The output will be:
0
1
2
3
4
6
7
8
9
10
where only the number 5
is skipped.
Live-code example
References
- Docs, M. (2015, July 20). continue (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/continue
Control Structures
goto
The goto
statement transfers the control of the program immediately to a labeled statement (Docs, 2015).
Even though goto
is useful in jumping out of a deeply nested loop, the use of goto statement is highly discouraged as it often results in spaghetti code:
…widespread and unconstrained use of GOTO statements has led to programmers producing inconsistent, incomplete and generally unmaintainable programs.
Such code is often known as ‘spaghetti’, given its convoluted and tangled control structure.
The previous nested for
loop
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (j > i)
{
break;
}
Console.Write($"{j}");
}
Console.WriteLine();
}
can be written using goto
as
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (j > i)
{
goto outer;
}
Console.Write($"{j}");
}
outer:
Console.WriteLine();
}
Note that we need a label (outer:
in the example above) to indicate the position we want the program control to go.
Live-code example
References
- Docs, M. (2015, July 20). goto (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/goto
- Cram, D., & Hedley, P. (2005). Pronouns and procedural meaning: The relevance of spaghetti code and paranoid delusion. Oxford University Working Papers in Linguistics, Philology and Phonetic, 10, 187–210.
Subprograms
Methods definition
Subprograms, or subroutines, are block of code that contains a series of statements and can be invoked from other remote locations in a program (Shute, n.d.).
In C#, subprograms are known as “methods” and consists of (Point, 2018):
- access modifier
- return type
- method name
- parameter list
- method body
There are also other optional modifiers like abstract
, virtual
, async
, override
, partial
, static
, etc.
<access modifier> <return type> <method name>(parameter list) {
method body
}
Here is a simple example of a method called Max
that receives 2 parameters first
and second
and returns an int
.
public int Max(int first, int second)
{
if (first > second)
{
return first;
}
return second;
}
Access modifier
Access modifiers control whether a particular method can be used from other assemblies, from other classes or structs, or inside derived classes. There are 5 types of accessibility levels in C# (Docs, 2015):
public
private
protected
internal
protected internal
private internal
Return type
A method may return a value to the caller and the return type of the method is the data type of the value being returned. If the method does not return any value, the return type will be void
.
The return
keyword is used to terminate the execution of current method and transfer the control back to the caller, optionally returning a value. However, return
is not required for void
methods (Docs, 2015).
For example, this is a method Max
that returns an int
:
public int Max(int first, int second)
{
if (first > second)
{
return first;
}
return second;
}
Here is a void
method that uses a return
statement for early exiting of the function. The pattern is commonly known as “guard clauses” (Cunningham & Cunningham, n.d.).
public void Initialize(string name, int age)
{
if (string.IsNullOrEmpty(name)) return;
if (age < 0) return;
// ... other operations
}
Parameter list
Parameters are used for passing data to the method. A parameter list is a list of parameters along with their:
- name
- type
that are enclosed within a pair of parenthesis. The order of the parameters in the list is also important to the caller. Note that parameters are optional; that is - a method may not receive any parameters.
Parameters vs. Arguments
The method definition specifies the parameters that are required; the caller specifies the arguments that contains the concrete values for the parameters.
In the following example: a
is the argument (from the caller side); while num
is a parameter (for the method Square
).
void Main()
{
int a = 1;
// a is the argument
Square(a);
}
// num is a parameter
int Square(int num)
{
return num * num;
}
References
- Shute, G. Subprograms. Retrieved from https://www.d.umn.edu/ gshute/asm/subprograms.xhtml
- Point, T. (2018). C# - Methods. Retrieved from https://www.tutorialspoint.com/csharp/csharp_methods.htm
- Docs, M. (2015, July 20). Access Modifiers (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers
- Docs, M. (2015, July 20). return (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/return
- Cunningham & Cunningham, I. Guard Clause. Retrieved from http://wiki.c2.com/?GuardClause
Subprograms
Positional and keyword parameters
In C#, arguments are bound to parameters based on their position (or order) by default; but C# also supports keyword parameters through the use of named arguments (Docs, 2015).
Take this Add
method as an example:
int Add(int first, int second, int third)
{
return first + second + third;
}
Positional
This is the default way of how arguments and parameters are bound to each other:
void Main()
{
int result = 0;
result = Add(1, 2, 3);
Console.WriteLine(result);
}
Named
The caller can specify the value of a parameter by associating the argument with the parameter’s name in the following format:
<parameter name>: <argument value>
The same method previously can also be invoked with:
result = Add(first: 1, second: 2, third: 3);
or even out-of-order:
result = Add(third: 3, second: 2, first: 1);
However, named arguments must be placed before any fix arguments. Thus the following code is invalid:
// ERROR: CS8323 Named argument is used out-of-position but is followed by an unnamed argument
result = Add(third: 3, 2, 1);
Live-code example
Named arguments are commonly used to convey the intent of the argument. Without looking at the definition of the method, which one is better?
Init(true, false, true);
vs.
Init(isProcessDataRequired: true,
isDiplayDataRequired: false,
isSaveDataRequired: true);
Optional
Optional arguments allow the caller to omit arguments for some parameters. The omitted parameter will use the default value defined in the method header.
For example:
void Print(string required, string optionalString = "Default string", int optionalNumber = 123)
{
Console.WriteLine($"{required}, {optionalString}, {optionalNumber}");
}
Can be called with:
Print("One");
Print("One", "Two");
Print("One", "Two", 3);
Output:
One, Default string, 123
One, Two, 123
One, Two, 3
By default, the arguments are positional, but named arguments can also be used to specify the value of the optional parameter.
For example, to skip the second parameter optionalString
:
Print("One", optionalNumber: 3);
Output:
One, Default string, 3
Live-code example
References
- Docs, M. (2015, July 20). Named and Optional Arguments (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments
Subprograms
Passing parameters
In C#, arguments can be passed to the parameters either
- by value, or
- by reference
Passing arguments by reference allows the changes made by the methods to be persisted at the calling site.
Pass by reference can be achieved by using the keyword ref
and out
, which we have covered in the topic “Functional side effects”
Do note that there are slight differences among
- passing value-type by value
- passing reference-type by value
- passing value-type by reference
- passing reference-type by reference
Value-type vs. reference-type
A value-type variable directly contains the data as compared to reference-type, which contains a reference (or pointer) to the data.
Value-types include:
- numeric types (
int
,long
,byte
,float
,double
, etc) bool
- structs
- enumerations
Reference-types include
- class
- interface
- object
- string
- ..etc
Passing value-type by value
When a value-type is passed by value, any changes to the parameter in the method will not be persisted back in the calling environment (Docs, 2015).
Passing reference-type by value
When a reference-type is passed by value, any changes to the reference/pointer/memory location of the parameter are local; but content of the parameter can be changed (Docs, 2015).
Changing the reference of the array parameter (ie. assigning it to a new array) will not be persisted:
void ChangeReference(int[] nums)
{
nums = new int[]{1, 2, 3, 4, 5};
}
void Main()
{
int[] numbers = new int[] { 10, 20, 30, 40 };
Console.WriteLine($"Original: {string.Join(", ", numbers)}");
ChangeReference(numbers);
Console.WriteLine($"After: {string.Join(", ", numbers)}");
}
Output:
Original: 10, 20, 30, 40
After: 10, 20, 30, 40
But changing the content of the array will be persisted:
void Main()
{
int[] numbers = new int[] { 10, 20, 30, 40 };
Console.WriteLine($"Original: {string.Join(", ", numbers)}");
ChangeContent(numbers);
Console.WriteLine($"After: {string.Join(", ", numbers)}");
}
void ChangeContent(int[] nums)
{
nums[0] = 1;
nums[1] = 2;
}
Output:
Original: 10, 20, 30, 40
After: 1, 2, 3, 40
Live-code example
Passing value-type by reference
When a value-type is passed by value, any changes to the parameter in the method will be persisted (Docs, 2015).
This can be achieved by using either ref
or out
keyword:
void Main()
{
int result;
Add(1, 2, out result);
Console.WriteLine(result);
}
void Add(int first, int second, out int result)
{
result = first + second;
}
Output:
3
Live-code example
Passing reference-type by reference
When a reference-type is passed by reference, any changes to the content and the reference/pointer/memory location of the parameter will be persisted (Docs, 2015).
In this case, changes to the reference of the array or the content of the array will both be persisted:
public static void Main()
{
int[] numbers = new int[]{10, 20, 30, 40};
int[] oldNumbers = numbers; // store a temporary reference to numbers
Console.WriteLine("--- Change reference/pointer/memory location");
Console.WriteLine($"Original: {string.Join(", ", numbers)}");
ChangeReference(ref numbers);
Console.WriteLine($"After: {string.Join(", ", numbers)}");
Console.WriteLine($"Still the same object? {numbers.Equals(oldNumbers)}");
oldNumbers = numbers;
Console.WriteLine();
Console.WriteLine("--- Change content");
Console.WriteLine($"Original: {string.Join(", ", numbers)}");
ChangeContent(ref numbers);
Console.WriteLine($"After: {string.Join(", ", numbers)}");
Console.WriteLine($"Still the same object? {numbers.Equals(oldNumbers)}");
}
static void ChangeReference(ref int[] nums)
{
nums = new int[]{1, 2, 3, 4, 5};
}
static void ChangeContent(ref int[] nums)
{
nums[0] = 100;
nums[1] = 200;
}
Output:
--- Change reference/pointer/memory location
Original: 10, 20, 30, 40
After: 1, 2, 3, 4, 5
Still the same object? False
--- Change content
Original: 1, 2, 3, 4, 5
After: 1, 2, 3, 4, 5
Still the same object? True
Live-code example
References
- Docs, M. (2015, July 20). Passing Parameters (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-parameters
- Docs, M. (2015, July 20). Value Types (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/value-types
- Docs, M. (2015, July 20). Reference types (C# Reference). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types
- Docs, M. (2015, July 20). Passing Value-Type Parameters (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-value-type-parameters
- Docs, M. (2015, July 20). Passing Reference-Type Parameters (C# Programming Guide). Retrieved from https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-reference-type-parameters
Subprograms
Method overloading
Overloading is a process where multiple methods with the same name but different signatures are defined. In C#, overloads are resolved at compile-time. Note that the return type of a method is not considered to be a part of a method’s signature (Skeet, n.d.).
Example:
void Foo(int x)
{
Console.WriteLine("Foo(int x)");
}
void Foo(string x)
{
Console.WriteLine("Foo(string x)");
}
Calling with
Foo(1);
Foo("String");
will output:
Foo(int x)
Foo(string x)
Live-code example
References
- Skeet, J. Overloading. Retrieved from http://csharpindepth.com/Articles/General/Overloading.aspx