Complete C# to Kotlin Syntax Comparisons
Tags: csharp, kotlin, android, beginners
Published: 2021-10-16 00:35:00

**Kotlin Cheat Sheet for C# developers**

## Introduction
If you are a C# developer and new to Kotlin, this article is for you. It gives you a quick overview of syntax comparisons between C# and Kotlin. It also can be served as your quick reference guide for Kotlin syntax.

*Please note that the standard naming conventions and coding styles for these 2 languages are also different. You can see the differences in the following code examples.* 

*If you have difficulty understanding the Kotlin syntax, please refer to the examples from [kotlinlang.org](https://vtsen.hashnode.dev/best-way-to-learn-kotlin-for-experienced-programmers) .*

## Methods vs Functions
**C#**
```c#
public virtual void PrintMessageWithPrefix(  
    String message,   
    String prefix = "Info")  
{  
     Console.WriteLine($"{prefix} {message}");  
}
```

**Kotlin**
```kotlin
fun printMessageWithPrefix(  
    message: String,   
    prefix: String = "Info") {

    println("[$prefix] $message")  
}
```

If the visibility modifier is not stated in Kotlin, it is `public` and `virtual` by default.

## Variables
**C#**
```c#
string a = "initial";  
const int b = 1;  
const int c = 3;
```

**Kotlin**
```kotlin
var a: String = "initial"   
val b: Int = 1               
val c = 3       
```

Both `var` in C# and Kotlin are similar. There is no `val` concept in C#. The closest thing is `const`and `readonly`.


## Null Safety
**C#**
```c#
string nullable = "You can keep a null here";  
nullable = null // okay
```

**Kotlin**
```kotlin
var neverNull: String = "This can't be null"  
neverNull = null // compilation error

var nullable: String? = "You can keep a null here"  
nullable = null // okay
```

There is no Null Safety in C#. In Kotlin, variable is not nullable by default unless you specify `?` in your variable declaration.

## Classes
**C#**
```c#
public class Contact  
{  
    public int id;  
    public string email;

    public Contact(int id, string email)  
    {  
        this.id = id;  
        this.email = email;  
    }  
}
```

**Kotlin**
```kotlin
class Contact(val id: Int, var email: String)
```

Can you see how amazing Kotlin is?

## Generics
**C#**
```c#
public class GenericList<T>  
{  
    public void Add (T data)  
    {  
    }  
}

public GenericList<T> CreateGenericList<T>()  
{  
    return new GenericList<T>();  
}
```

**Kotlin**
```kotlin
class GenericList<T>() {  
    fun add (data: T) {  
    }  
}  
   
fun <T> createGenericList() = GenericList<T>()
```

## Inheritance
**C#**
```c#
public class Dog  
{  
    public virtual void SayHello()  
    {  
        Console.WriteLine("wow wow!");  
    }  
}

public class Yorkshire : Dog 
{  
    public override void SayHello()  
    {  
        Console.WriteLine("wif wif!");  
    }  
}
```

**Kotlin**
```kotlin
open class Dog {                  
    open fun sayHello() {         
        println("wow wow!")  
    }  
}

class Yorkshire : Dog() {         
    override fun sayHello() {  
        println("wif wif!")  
    }  
}
```

Kotlin's classes and functions are `final` and `virtual`  by default. `open` modifier is required to allow inheritance.

## Switch vs When Statement
**C#**
```c#
void SwitchFunction(int input)  
{  
    switch (input)  
    {  
        case 0:  
            break;  

        default:  
             break;  
    }  
}
```

**Kotlin**
```kotlin
fun SwitchFunction(input: Int) {  
    when (input) {  
        0 -> {  
        }  
        else -> {  
        }  
    }  
}
```

## Switch vs When Expression
**C#**
```c#
var input = 1;  
var output = input switch  
{  
    1 => "one",  
    2 => "two",  
    _ => "too big",  
};
```

**Kotlin**
```kotlin
val input = 1  
val output = when (input) {  
    1 -> "one"  
    2 -> "two"  
    else -> "too big"  
}
```


## Loops
**C#**
```c#
var cakes = new List<String>() { "carrot", "cheese", "chocolate" };

// for loop
foreach (var cake in cakes)
{
    Console.WriteLine($"Yummy, it's a {cake} cake!");
}

// while  
var cakesEaten = 0;
while (cakesEaten < cakes.Count)
{
    ++cakesEaten;
}

// do-while  
cakesEaten = 0;
do
{
    ++cakesEaten;
} while (cakesEaten < cakes.Count);
```

**Kotlin**
```kotlin
val cakes = listOf("carrot", "cheese", "chocolate")  
  
// for loop  
for (cake in cakes) {  
    println("Yummy, it's a $cake cake!")  
}  
  
// while  
var cakesEaten = 0  
while (cakesEaten < cakes.size) {  
    ++cakesEaten  
}  
  
// do-while  
cakesEaten = 0  
do {                              
    ++cakesEaten  
} while (cakesEaten < cakes.size)
```


## Ranges
**C#**
```c#
foreach (var index in Enumerable.Range(1, 5))
{ 
	Console.WriteLine(index); 
}
```

**Kotlin**
```kotlin
for(index in 1..5) { 
    print(index)
}
```

## Equality Checks
**C#**
```c#
var authors = new HashSet<string>() 
    { "Shakespeare", "Hemingway", "Twain" };
var writers = new HashSet<string>() 
    { "Twain", "Shakespeare", "Hemingway" };

Console.WriteLine(authors.SetEquals(writers)); // return true
Console.WriteLine(authors == writers);  // return false
```

**Kotlin**
```kotlin
val authors = setOf("Shakespeare", "Hemingway", "Twain")
val writers = setOf("Twain", "Shakespeare", "Hemingway")

println(authors == writers)   // return true
println(authors === writers)  // return false
```

The important thing here `==` in Kotlin is a structural comparison and `===` is a reference comparison.  In C#, `==` (similar to `Equals()` API) is reference comparison. For structural comparison in C#, specify API needs to be called (e.g. `SetEquals()`) as in the above example.


## Conditional Expression
**C#**
```c#
 int Max(int a, int b) => a > b ? a : b;

 Console.WriteLine(Max(99, -42));
```

**Kotlin**
```kotlin
fun max(a: Int, b: Int) = if (a > b) a else b 

println(max(99, -42))
```
No ternary operator `condition ? then : else` in Kotlin.


## Record vs Data Classes
**C#**
```c#
// available only in C# 9
public record User{
	public  int Id { get; set; } 
	public  string Name { get; set; } 
}
```

**Kotlin**
```kotlin
data class User(val id: Int, val name: String) {
}
```

## Enum Classes
**C#**
```
enum State
{
    IDLE, RUNNING, FINISHED             
}
```

**Kotlin**
```kotlin
enum class State {
    IDLE, RUNNING, FINISHED                        
}
```

## Sealed Classes
**C#**
```c#
public sealed class Mammal {}   
```

**Kotlin**
```kotlin
sealed class Mammal()   
```

There are similar but `sealed class` in Kotlin still allow you to subclass as long as it is still within the same package. In C# `sealed` means 100% sealed in all scenarios.


## Static vs Object Keyword
**C#**
```c#
//no object expression in C# 
public class DayRates
{
    int standard = 30;
}
var dayRates = new DayRates();

// static class and method
static class DoAuth
{                       
	static void DoSomething() 
	{  
	}  
}

// static class/method within another class
class Server
{
    static class DoAuth
    {
        static void DoSomething()
        {
        }
    }
}
```

**Kotlin**
```kotlin
//object expression  
val dayRates = object {  
    var standard: Int = 30  
}

// object declaration (similar to static class/method)  
object DoAuth {                             
    fun doSomething() {  
    }  
}

// when object delaration is used inside a class
// companion object is used
class Server {  
    companion object DoAuth {  
		fun doSomething() {  
        }  
    }  
}
```
**Updated: May 21, 2022** - The following article shows how `object` keyword can reduce plenty of boilerplate code.
- [How to Create Singleton Class in Kotlin?](https://vtsen.hashnode.dev/how-to-create-singleton-class-in-kotlin)

## Higher-Order Methods / Functions
**C#**
```c#
// method that takes operation has higher-order method
public int Calculate(int x, int y, Func<int, int, int> operation)
{
    return operation(x, y);
}

// method to be passed in as higher-order method
public int Sum(int x, int y)
{
    return x + y;
}

// usage
int sumResult = Calculate(1, 2, Sum)
```

**Kotlin**
```kotlin
// function that takes operation has higher-order function
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {  
    return operation(x, y)                                        
}

// function to be passed in as higher-order function
fun sum(x: Int, y: Int) = x + y          

// usage
val sumResult = calculate(1, 2, ::sum)    
```

## Lambda Methods / Functions
**C#**
```c#
Func<String, String> UpperCase 
	= (String str) => { return str.ToUpper(); };   
```

**Kotlin**
```kotlin
val upperCase = { str: String -> str.uppercase() }     
```

## Extension Methods / Functions
**C#**
```c#
// implement int.IsBig() extention method
public static class Extensions
{
    public static bool IsBig(this int value)
    {
        return value > 100;
    }
 }
 
 // usage
 int value = 1000;
 Console.WriteLine(value.IsBig()); 
```

**Kotlin**
```kotlin
// implement Int.isBig() extention function
fun Int.isBig() :Boolean {  
    return this > 100  
}

// usage
val value = 1000  
println(value.isBig()) 
```

## List / IReadOnlyList vs MutableList / List
**C#**
```c#
// mutable list
List<int> systemUsers = new List<int> { 1, 2, 3 };
// immutable list
IReadOnlyList<int> sudoers = new List<int> { 1, 2, 3 };
```

**Kotlin**
```kotlin
// mutable list
val systemUsers: MutableList<Int> = mutableListOf(1, 2, 3)        
// immutable list
val sudoers: List<Int> = systemUsers                              
```
Kotlin `List` is immutable by default and C#`List` is mutable by default.


## HashSet / ImmutableHashSet vs MutableSet / Set
**C#**
```c#
var openIssues = new HashSet< String> 
    { "uniqueDescr1", "uniqueDescr2", "uniqueDescr3"};

var immutableOpenIssues = ImmutableHashSet.Create<String>();
immutableOpenIssues.Add("uniqueDescr1");
immutableOpenIssues.Add("uniqueDescr2");
immutableOpenIssues.Add("uniqueDescr3");
```

**Kotlin**
```kotlin
val openIssues: MutableSet<String> = mutableSetOf(
	"uniqueDescr1", "uniqueDescr2", "uniqueDescr3") 

val immutableOpenIssues: Set<String> = setOf(
	"uniqueDescr1", "uniqueDescr2", "uniqueDescr3")
```

## IReadOnlyDictionary / Dictionary vs Map / MutableMap
**C#**
```c#
// immutable dictionary
IReadOnlyDictionary<string, string> occupations =
	new Dictionary<string, string>
	{
	    ["Malcolm"] = "Captain",
	    ["Kaylee"] = "Mechanic"
	};

// mutable dictionary
var occupationsMutable = new Dictionary<string, string>
{
    ["Malcolm"] = "Captain",
    ["Kaylee"] = "Mechanic"
};
```

**Kotlin**
```kotlin
// immutable Map
val occupations = mapOf( 
	"Malcolm" to "Captain", 
	"Kaylee" to "Mechanic" ) 

// mutable Map
val occupationsMutable = mutableMapOf( 
	"Malcolm" to "Captain", 
	"Kaylee" to "Mechanic" ) 
```

## Where vs filter
**C#**
```c#
var numbers = new List<int> { 1, -2, 3, -4, 5, -6 };

var positives = numbers.Where(x => x > 0);
var negatives = numbers.Where(x => x < 0);
```

**Kotlin**
```kotlin
val numbers = listOf(1, -2, 3, -4, 5, -6) 

val positives = numbers.filter { x -> x > 0 } 
val negatives = numbers.filter { it < 0 }
```

## Select vs map
**C#**
```c#
var numbers = new List<int> { 1, -2, 3, -4, 5, -6 };
var doubled = (List<int>)numbers.Select(x => x * 2);
```

**Kotlin**
```kotlin
val numbers = listOf(1, -2, 3, -4, 5, -6) 
val doubled = numbers.map { x -> x * 2 }
```
Please note that `map` and `Map` are different. `Map` is a dictionary collection and `map` is the extension functions of the collection.

## any, all, none, and etc.
**C#**
```c#
var numbers = new List<int> { 1, -2, 3, -4, 5, -6 };

var anyNegative = numbers.Any(x => x < 0);
var allEven = numbers.All(x => x % 2 == 0);
var allOdd = numbers.All(x => x % 2 != 0);
```

**Kotlin**
```kotlin
val numbers = listOf(1, -2, 3, -4, 5, -6)
           
val anyNegative = numbers.any { it < 0 } 
val allEven = numbers.all { it % 2 == 0 } 
val allOdd = numbers.none { it % 2 == 0 } 
```
`none` is not available in C# but you can achieve the same result with `All`

There are other similar extension functions which I do not list all of them here such as `find`, `findAll`, `first`, `last`, `count`, `associateBy`, `groupBy`, `partition`, `flatMap`, `minOrNull`, `maxOrNull`, `sorted`, `[]` - map element access, `zip` and `getOrElse`. C# may or may not have the equilvalent functions.


## let, run, with, apply, also 
**C#**
```c#
public void CustomPrint(String str)
{
    Console.WriteLine($"Custom: {str}");
}

var myStr = "test";

// no scope functions in C#
CustomPrint(myStr);
var empty = myStr.Length == 0;
```

**Kotlin**
```kotlin
fun customPrint(str: String) {  
    println("Custom: $str")  
} 

var myStr = "test"  
// let scope function
var empty = myStr.let {  
	customPrint(it)  
    it.isEmpty() // return the last expression  
}  

// run scope funciton
empty = myStr.run {  
	customPrint(this)  
    isEmpty() // return the last expression 
}

// with scope function
empty = with(myStr) {  
  customPrint(this)  
    isEmpty()  // return the last expression
}

// apply scope function
empty = myStr.apply {  
  customPrint(this)  
}.isEmpty()  

// also scope function  
empty = myStr.also {  
  customPrint(it)  
}.isEmpty() 
```
There are no scope methods in C# and it needs custom implementation if we need one.

As you can see, these scope functions are very similar. In fact, there are interchangeablein my opinion. I do not 100% know how to use them. However, I think there are conventions on how to use them (will be covered by another blog post).

> **[Updated - Jan 11, 2022]:** - For my usage recommendations, see the article below:

- [Simple Way to Understand Kotlin Scope Functions - let, run, with, apply, also](https://vtsen.hashnode.dev/simple-way-to-understand-kotlin-scope-functions-let-run-with-apply-also)


## by lazy - Delegated Properties
**C#**
```c#
// no equivalent delegated properties in C#
string lazyStr = null;
public string LazyStr
{
    get
    {
        if (lazyStr == null)
        {
            Console.WriteLine("computed!");
            lazyStr = "my Lazy";
        }
        return lazyStr;
    }
}
```

**Kotlin**
```kotlin
val lazyStr: String by lazy {
   println("computed!") 
   "my lazy"
}
```

## Summary

In my opinion, Kotlin is more powerful than C#.  It reduces the use of boilerplate code. C# can accomplish the same thing with more codes. I hope these comparisons help.

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/complete-c-to-kotlin-syntax-comparisons)*.