println("Hello, world!")
public void Main()
{
Console.WriteLine("Hello, world!");
}
// C# 9 supports top-level statements
Console.WriteLine("Hello, world!");
var myVariable = 42
myVariable = 50
val myConstant = 42
var myVariable = 42;
myVariable = 50;
// C# doesn't have local runtime constants
val explicitDouble: Double = 70.0
double explicitDouble = 70.0;
val label = "The width is "
val width = 94
val widthLabel = label + width
// The width is 94
var label = "The width is ";
var width = 94;
var widthLabel = label + width;
// The width is 94
const val SYSTEM_DEPRECATED: String = "System is deprecated"
const string SYSTEM_DEPRECATED = "System is deprecated";
val apples = 3
val oranges = 5
val fruitSummary = "I have ${apples + oranges} " +
"pieces of fruit."
var apples = 3;
var oranges = 5;
var fruitSummary = $"I have {apples + oranges} " +
"pieces of fruit.";
val age = 42
if (age < 10) {
println("You're too young to watch this movie")
} else if (age < 13) {
println("You can watch this movie with a parent")
} else {
println("You can watch this movie")
}
var age = 42;
if (age < 10)
{
Console.WriteLine("You're too young to watch this movie");
}
else if (age < 13)
{
Console.WriteLine("You can watch this movie with a parent");
}
else
{
Console.WriteLine("You can watch this movie");
}
// if is an expression, so ternary operation not needed
val loaded = true
val status = if (loaded) "Ready" else "Loading..."
// "Ready
var loaded = true;
var status = loaded ? "Ready" : "Loading...";
// "Ready
fun greet(name: String, day: String): String {
return "Hello $name, today is $day."
}
val text = greet("Bob", "Tuesday")
// Hello Bob, today is Tuesday.
string Greet(string name, string day)
{
return $"Hello {name}, today is {day}.";
}
var text = Greet("Bob", "Tuesday");
// Hello Bob, today is Tuesday
// Single expression functions can be without braces and return type
fun double(value: Int) = value * 2
val result = double(4)
// 8
// Single expression functions can be without braces
int Double(int value) => value * 2;
var result = Double(4);
// 8
fun area(width: Int, height: Int) = width * height
var result = area(width = 2, height = 3)
// This is also possible with named arguments
result = area(2, height = 2)
result = area(height = 3, width = 2)
int Area(int width, int height) => width * height;
var result = Area(width: 2, height: 3);
// This is also possible with named arguments
result = Area(2, height: 2);
result = Area(height: 3, width: 2);
fun displayGreeting(message: String, name: String = "Guest") {
println("Hello $name, $message")
}
displayGreeting("welcome!")
// Hello Guest, welcome!
void DisplayGreeting(string message, string name = "Guest")
{
Console.WriteLine($"Hello {name}, {message}");
}
DisplayGreeting("welcome!");
// Hello Guest, welcome!
fun sumOf(vararg numbers: Int): Int {
var sum = 0
for (number in numbers) {
sum += number
}
return sum
}
val sum = sumOf(42, 597, 12)
// sumOf() can also be written in a shorter way:
fun sumOf(vararg numbers: Int) = numbers.sum()
int SumOf(params int[] numbers)
{
var sum = 0;
foreach (var number in numbers)
sum += number;
return sum;
}
var sum = SumOf(42, 597, 12);
// SumOf() can also be written in a shorter way:
int SumOf(params int[] numbers) => numbers.Sum();
fun containsEven(numbers: List<Int>) = numbers.any { it % 2 == 0 }
bool ContainsEven(List<int> numbers) => numbers.Any(e => e % 2 == 0);
fun makeIncrementer(): (Int) -> Int {
val addOne = fun(number: Int): Int {
return 1 + number
}
return addOne
}
val increment = makeIncrementer()
val result = increment(7)
// makeIncrementer can also be written in a shorter way:
fun makeIncrementer() = fun(number: Int) = 1 + number
Func<int, int> MakeIncrementer()
{
int addOne(int number) => 1 + number;
return addOne;
}
var increment = MakeIncrementer();
var result = increment(7);
// MakeIncrementer can also be written in a shorter way:
Func<int, int> MakeIncrementer() => i => 1 + i;
fun transform(initial: String, f: (String) -> String) = f(initial)
val result = transform("hello", { x -> x.toUpperCase() })
// HELLO
// Trailing lambda can be placed outside the parentheses
val result2 = transform("hello") { x -> x.toUpperCase() }
string Transform(string initial, Func<string, string> f) => f(initial);
var result = Transform("hello", x => x.ToUpper());
// HELLO
// Kotlin doesn't have tuples, use data classes
data class GasPrices(val a: Double, val b: Double, val c: Double)
fun getGasPrices() = GasPrices(3.59, 3.69, 3.79)
val prices = getGasPrices();
val (a, b, c) = getGasPrices();
(double a, double b, double c) GetGasPrices() => (3.59, 3.69, 3.79);
var result = GetGasPrices();
var (a, b, c) = GetGasPrices();
val shoppingList = arrayOf("catfish", "water",
"tulips", "blue paint")
shoppingList[1] = "bottle of water"
var shoppingList = new[] { "catfish", "water",
"tulips", "blue paint" };
shoppingList[1] = "bottle of water";
val shoppingList = listOf("catfish", "water",
"tulips", "blue paint")
val shoppingListMutable = mutableListOf("catfish", "water",
"tulips", "blue paint")
shoppingListMutable[1] = "bottle of water"
shoppingListMutable.add("bucket")
IReadOnlyList<string> shoppingList = new List<string> { "catfish",
"water", "tulips", "blue paint" };
var shoppingListMutable = new List<string> { "catfish", "water",
"tulips", "blue paint" };
shoppingListMutable[1] = "bottle of water";
shoppingListMutable.Add("bucket");
val occupations = mapOf(
"Malcolm" to "Captain",
"Kaylee" to "Mechanic"
)
val occupationsMutable = mutableMapOf(
"Malcolm" to "Captain",
"Kaylee" to "Mechanic"
)
occupationsMutable["Jayne"] = "Public Relations"
occupationsMutable.put("Rick", "Navigation")
IReadOnlyDictionary<string,string> occupations =
new Dictionary<string, string>
{
["Malcolm"] = "Captain",
["Kaylee"] = "Mechanic"
};
var occupationsMutable = new Dictionary<string, string>
{
["Malcolm"] = "Captain",
["Kaylee"] = "Mechanic"
};
occupationsMutable["Jayne"] = "Public Relations";
occupationsMutable.Add("Rick", "Navigation");
val emptyList = mutableListOf<String>()
val emptyMap = mutableMapOf<String, Float>()
// read-only empty list
val empty = emptyList<String>()
var emptyList = new List<string>();
var emptyDictionary = new Dictionary<string, float>();
// read-only empty list
var empty = Enumerable.Empty<string>();
val names = arrayOf("Anna", "Alex", "Brian", "Jack")
for (name in names) {
println("Person is called $name")
}
names.forEach { println("Person is called $it") }
// Person is called Anna
// Person is called Alex
// Person is called Brian
// Person is called Jack
var names = new List<string> { "Anna", "Alex", "Brian", "Jack" };
foreach (var name in names)
{
Console.WriteLine($"Person is called {name}");
}
names.ForEach(name => Console.WriteLine($"Person is called {name}"));
// Person is called Anna
// Person is called Alex
// Person is called Brian
// Person is called Jack
val names = arrayOf("Anna", "Alex", "Brian", "Jack")
val count = names.count()
for (i in 0..
var names = new[] { "Anna", "Alex", "Brian", "Jack" };
var count = names.Count();
foreach (var i in Enumerable.Range(0, count))
{
Console.WriteLine($"Person {i + 1} is called {names[i]}");
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack
for (index in 1..5) {
println("$index times 5 is ${index * 5}")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
foreach (var index in Enumerable.Range(1, 5))
{
Console.WriteLine($"{index} times 5 is {index * 5}");
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
val names = arrayOf("Anna", "Alex", "Brian", "Jill", "Jack")
val count = names.count()
for (name in names.slice(1..
var names = new[] { "Anna", "Alex", "Brian", "Jill", "Jack" };
foreach (var name in names[1..^1])
{
Console.WriteLine($"Person is called {name}");
}
// Person is called Alex
// Person is called Brian
// Person is called Jill
val numbers = listOf(20, 19, 7, 12)
val multiplied = numbers.map { 3 * it }
// [ 60, 57, 21, 36 ]
var numbers = new[] { 20, 19, 7, 12 };
var multiplied = numbers.Select(e => 3 * e);
// [ 60, 57, 21, 36 ]
val ordered = listOf(1, 5, 3, 12, 2).sorted()
// [ 1, 2, 3, 5, 12 ]
var ordered = new[] { 1, 5, 3, 12, 2 }.OrderBy(i => i);
// [ 1, 2, 3, 5, 12 ]
val datas = listOf(
SensorData(1, "A", 2.89),
SensorData(2, "B", 12.01),
SensorData(3, "B", 11.89),
SensorData(4, "A", 3.11),
SensorData(5, "A", -456.0)
)
val avgs = datas
.filter { it.value > -50.0 }
.groupBy(SensorData::location)
.map { Location(it.key, it.value.map(SensorData::value).average()) }
// (location=A, value=3.0)
// (location=B, value=11.95)
var datas = new List<SensorData>
{
new SensorData { Id = 1, Location = "A", Value = 2.89 },
new SensorData { Id = 2, Location = "B", Value = 12.01 },
new SensorData { Id = 3, Location = "B", Value = 11.89 },
new SensorData { Id = 4, Location = "A", Value = 3.11 },
new SensorData { Id = 5, Location = "A", Value = -456.0 }
};
var avgs = datas
.Where(e => e.Value > -50.0)
.GroupBy(e => e.Location)
.Select(g => new {
Location = g.Key,
Value = g.Average(e => e.Value) });
// { Location = A, Value = 3.0 }
// { Location = B, Value = 11.95 }
// Sequence is lazy
val seq = sequenceOf(1, 2, 3, 4)
.filter { println("Filter $it, "); it % 2 == 1 }
.map { println("Map $it, "); it * 2 }
// Computations are evaluated during terminal operation
val items = seq.toList()
// Filter 1,
// Map 1,
// Filter 2,
// Filter 3,
// Map 3,
// Filter 4,
// List is not lazy, so functions are evaluated immediately
val items2 = listOf(1, 2, 3, 4)
.filter { println("Filter $it, "); it % 2 == 1 }
.map { println("Map $it, "); it * 2 }
// Filter 1,
// Filter 2,
// Filter 3,
// Filter 4,
// Map 1,
// Map 3,
// LINQ is lazy, so no need to use other collection types
var query = new List { 1, 2, 3, 4 }
.Where(i =>
{
Console.WriteLine($"Filter {i}, ");
return i % 2 == 1;
}).Select(i =>
{
Console.WriteLine($"Map {i}, ");
return i * 2;
});
var items = query.ToList();
//Filter 1,
//Map 1,
//Filter 2,
//Filter 3,
//Map 3,
//Filter 4,
class Shape {
var numberOfSides = 0
fun simpleDescription() =
"A shape with $numberOfSides sides."
}
class Shape
{
public int NumberOfSides { get; set; }
public string SimpleDescription() =>
$"A shape with {NumberOfSides} sides.";
}
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
// A shape with 7 sides.
var shape = new Shape();
shape.NumberOfSides = 7;
var shapeDescription = shape.SimpleDescription();
// A shape with 7 sides.
open class NamedShape(val name: String) {
var numberOfSides = 0
open fun simpleDescription() =
"A shape with $numberOfSides sides."
}
class Square(var sideLength: Double, name: String) :
NamedShape(name) {
init {
numberOfSides = 4
}
fun area() = sideLength.pow(2)
override fun simpleDescription() =
"A square with sides of length $sideLength."
}
val square = Square(5.2, "My square")
val area = square.area()
val desc = square.simpleDescription()
class NamedShape
{
private readonly string _name;
public NamedShape(string name) => _name = name;
protected int NumberOfSides { get; set; }
public virtual string SimpleDescription() =>
$"A shape with {NumberOfSides} sides.";
}
class Square: NamedShape
{
private readonly double _sideLength;
public Square(double sideLength, string name) : base(name)
{
_sideLength = sideLength;
NumberOfSides = 4;
}
public double Area() => Math.Pow(_sideLength, 2);
override public string SimpleDescription() =>
$"A square with sides of length {_sideLength}.";
}
var square = new Square(5.2, "My square");
var area = square.Area();
var desc = square.SimpleDescription();
data class Customer(var id: Long, var name: String)
val customer = Customer(1, "Sachin")
val name = customer.name
customer.id = 2
// Pre C# 9 doesn't have data classes
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
var customer = new Customer { Id = 1, Name = "Sachin" };
var name = customer.Name;
customer.Id = 2
// C# 9 has records
// Records can be mutable, but they are primarily
// intended for supporting immutable data models
public record Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
data class Customer(val id: Long, val name: String)
val customer = Customer(1, "Sachin")
val name = customer.name
customer.id = 2 // Error
// C# 9 immutable records can be created with positional parameters
public record Customer(int Id, string Name);
Customer customer = new(1, "Sachin");
customer.Id = 2 // Error
// or with standard property syntax and init only setter
public record Customer
{
public int Id { get; init; }
public string Name { get; init; }
};
var customer = new Customer { Id = 1, Name = "Sachin" };
customer.Id = 2 // Error
// Pre C# 9
public class Customer
{
public Customer(int id, string name) => (Id, Name) = (id, name);
public int Id { get; }
public string Name { get; }
}
var customer = new Customer(1, "Sachin");
var name = customer.Name;
customer.Id = 2 // Error
fun MutableList<Int>.swap(idx1: Int, idx2: Int) {
val tmp = this[idx1]
this[idx1] = this[idx2]
this[idx2] = tmp
}
val list = mutableListOf(1, 5, 3)
list.swap(0, 2)
// [ 3, 5, 1 ]
public static class Extensions
{
public static void Swap(this List<int> list, int idx1, int idx2)
{
var temp = list[idx1];
list[idx1] = list[idx2];
list[idx2] = temp;
}
}
var list = new List<int> { 1, 5, 3 };
list.Swap(0, 2);
// [ 3, 5, 1 ]
interface Nameable {
fun name(): String
}
fun <T: Nameable> genericFunction(x: T) {
println("Name is " + x.name())
}
class Person : Nameable {
override fun name() = "Person A"
}
interface INameable
{
string Name();
}
void GenericMethod<T>(T x) where T : INameable
{
Console.WriteLine("Name is " + x.Name());
}
class Person : INameable
{
public string Name() => "Person A";
}
var movieCount = 0
var songCount = 0
for (item in library) {
if (item is Movie) {
++movieCount
} else if (item is Song) {
++songCount
}
}
var movieCount = 0;
var songCount = 0;
foreach (var item in library)
{
if (item is Movie)
++movieCount;
else if (item is Song)
++songCount;
}
// Unsafe (throw exception)
val circle: Circle = shape as Circle
// Safe (return null)
val circle: Circle? = shape as Circle?
val circle: Circle? = shape as? Circle
// Unsafe (throw exception)
var circle = (Circle)shape;
// Safe (return null)
var circle = shape as Circle;
// If Nullable reference types enabled (optional feature)
Circle? circle = shape as Circle;
for (current in library) {
if (current is Movie) {
println("Movie: '${current.name}', " +
"dir: ${current.director}")
}
}
foreach (var current in library)
{
if (current is Movie movie)
{
Console.WriteLine($"Movie: '{movie.Name}', " +
$"dir: {movie.Director}");
}
}
try {
// some code
}
catch (e: SomeException) {
if (e.SomeCode == 404) {
// handle SomeException when SomeCode is 404
} else {
// handle SomeException
}
}
catch (e: Exception) {
// handle rest of the Exceptions
}
finally {
// optional finally block
}
try
{
// Some code
}
catch (SomeException e) when (e.SomeCode == 404)
{
// Handle SomeException only when SomeCode is 404
}
catch (SomeException e)
{
// Handle SomeException
}
catch (Exception e)
{
// Handle rest of the Exceptions
}
finally
{
// Optional finally block
}
// try is an expression, i.e., it may have a return value
val a: Int? = try { input.toInt() }
catch (e: NumberFormatException) { null }
// try is not an expression
int? a;
try { a = int.Parse(input); }
catch { a = null; }
val nb = 42
val text = when (nb) {
in 0..7, 8, 9 -> "single digit"
10 -> "double digits"
in 11..99 -> "double digits"
in 100..999 -> "triple digits"
else -> "four or more digits"
}
// double digits
// Pre C# 9
var nb = 42;
var text = nb switch
{
int i when i < 10 => "single digit",
10 => "double digits",
int i when i < 100 => "double digits",
int i when i < 1000 => "triple digits",
_ => "four or more digits"
};
// With C# 9 relational and conjunctive patterns
var nb = 42;
var text = nb switch
{
< 10 => "single digit",
10 or (>= 11 and < 100) => "double digits",
< 1000 => "triple digits",
_ => "for or more digits",
};
// double digits
// Not supported yet
// https://youtrack.jetbrains.com/issue/KT-20004
// http://openjdk.java.net/jeps/305
var result = item switch
{
Square s => Handle(s),
Circle { Radius: < 10 } c => HandleUnder10(c),
Circle { Radius: 20 } c => Handle20(c),
Circle c => Handle(c),
_ => throw new Exception("Unknown shape")
};
// Same with if statements
if (item is Square s)
{ }
else if (item is Circle { Radius: < 10 })
{ }
else if (item is Circle { Radius: 20 })
{ }
else if (item is Circle ci)
{ }
data class Measurement(val celsius: Double)
val data: Measurement = null // Error: can't be null
val data: Measurement? = null // Ok: can be null
fun printMayBeNull(data: Measurement?) {
// data can be null, must have null check
if (data == null)
return
println(data.celsius)
}
fun printNoNull(data: Measurement) {
// data can't be null. No need for check
println(data.celsius)
}
val current: Measurement? = getDataFromApi()
printMayBeNull(current) // Ok: can be null
if (current == null)
return
printNoNull(current)
// Nullable reference types are optional feature in C#
public class Measurement
{
public double Celsius { get; set; }
}
Measurement? data = null; // Ok: can be null
Measurement data = null; // Error: can't be null
void PrintMayBeNull(Measurement? data)
{
// data can be null, must have null check
if (data == null)
return;
Console.WriteLine(data.Celsius);
}
void PrintNoNull(Measurement data)
{
// data can't be null. No need for check
Console.WriteLine(data.Celsius);
}
Measurement? current = GetDataFromApi();
PrintMayBeNull(current); // No need for check as method accepts nulls
if (current == null)
return;
PrintNoNull(current); // OK: Null is checked before method call
data class DataPoint(val id: Int, val celsius: Double,
val child: DataPoint? = null)
val data = DataPoint(1, 22.1, DataPoint(2, 22.8))
val result = data.child?.child?.
let { toFahrenheit(it.celsius) } ?: Double.MIN_VALUE
public class DataPoint
{
public int Id { get; set; }
public double Celsius { get; set; }
public DataPoint? Child { get; set; }
}
var data = new DataPoint
{
Id = 1,
Celsius = 22.1,
Child = new DataPoint { Id = 2, Celsius = 22.8 }
};
var result = data.Child?.Child != null
? ToFahrenheit(data.Child.Child.Celsius)
: double.MinValue;
val data = DataPoint(1, 22.1, DataPoint(2, 22.8))
val result = data.child?.child?.celsius ?: Double.MIN_VALUE
// Double.MIN_VALUE
var data = new DataPoint
{
Id = 1,
Celsius = 22.1,
Child = new DataPoint { Id = 2, Celsius = 22.8 }
};
var result = data.Child?.Child?.Celsius ?? double.MinValue;
// double.MinValue
// Use .let and forget weird helper methods
val data = DataPoint(1, 22.1, DataPoint(2, 22.8))
val result = data.child?.child?.
let { toFahrenheit(it.celsius) } ?: Double.MIN_VALUE
// Generic helper method that will return boolean and set output
bool GetValue<T>(T input, out T output)
{
output = input;
return output != null;
}
var data = new DataPoint
{
Id = 1,
Celsius = 22.1,
Child = new DataPoint { Id = 2, Celsius = 22.8 }
};
var result = GetValue(data.Child?.Child, out var output)
? ToFahrenheit(output.Celsius)
: double.MinValue;
string set = "My text";
var text = GetValue(set, out var output) ? output : "Not set";
// "My text"
string notSet = null;
var text = GetValue(notSet, out var output) ? output : "Not set";
// "Not set"
// The dynamic type is not supported in code targeting the JVM
// https://kotlinlang.org/docs/reference/dynamic-type.html
// JSON example with data classes
data class Work(val name: String, val location: String)
data class User(val id: String, val work: Work)
val json = """[
{ "id": "A", "work": { "name": "ACME 2", "location": "NY" } },
{ "id": "B", "work": { "name": "Box Co", "location": "SF" } },
{ "id": "C", "work": { "name": "DotCom", "location": "NY" } }
]"""
val users = jacksonObjectMapper().readValue<List<User>>(json)
val name = users.first().work.name
// ACME 2
val fromNy = users
.filter { it.work.location == "NY" }
.map { it.id }
// [A, C]
/* with dynamic, type is not known until runtime */
var json = @"[
{ 'id': 'A', 'work': { 'name': 'ACME 2', 'location': 'NY' } },
{ 'id': 'B', 'work': { 'name': 'Box Co', 'location': 'SF' } },
{ 'id': 'C', 'work': { 'name': 'DotCom', 'location': 'NY' } }
]";
var users = JsonConvert.DeserializeObject<List<dynamic>>(json);
var name = users.First().work.name;
// ACME 2
var fromNY = users
.Where(e => e.work.location == "NY")
.Select(e => e.id);
// [A, C]
data class Stats(val full_name: String, val stargazers_count: Int = -1, val forks: Int = -1)
val mapper = jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val repos = listOf("jetbrains/kotlin", "dotnet/csharplang")
val asyncRequests = repos.map { repo ->
GlobalScope.async {
val body = Fuel.get("https://api.github.com/repos/$repo")
.responseString()
.third.component1() // Fuel Result & Body
body?.let { mapper.readValue(it) } ?: Stats(repo)
}
}
runBlocking {
val results = asyncRequests.map { it.await() }
results.forEach{ println("${it.full_name} : ${it.stargazers_count} - ${it.forks}") }
}
var client = new HttpClient();
var repos = new [] { "jetbrains/kotlin", "dotnet/csharplang" };
var asyncRequests = repos.Select(async repo =>
{
var response = await client.GetAsync($"https://api.github.com/repos/{repo}");
var json = await response.Content.ReadAsStringAsync();
dynamic content = JsonConvert.DeserializeObject(json);
return new { repo, stars = content.stargazers_count, forks = content.forks };
});
var results = await Task.WhenAll(asyncRequests);
foreach(var data in results)
Console.WriteLine($"{data.repo} : {data.stars} - {data.forks}");