Kotlin и C#

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Когда речь заходит о сахаре и модных фичах в языках программирования, среди первых вариантов на ум приходят C# и Kotlin. Поскольку эти два языка занимают схожие ниши, то есть, строго типизированы, обладают сборкой мусора, кроссплатформенны, применяются как на бекенде, так и в мобильной разработке, то сегодня мы попытаемся сравнить их синтаксические возможности и устроить небольшое голосование. Чтобы сравнение прошло честно, будем рассматривать последние версии обоих языков. Оговорюсь о своей непредвзятости: мне одинаково нравятся оба языка, они находятся в непрерывном развитии и не отстают друг от друга. Эта статья является сравнительной, а не обучающей, поэтому некоторые заурядные синтаксические возможности могут быть опущены.

Начнем с точки входа

В C# эту роль играет статический метод Main или top-level entry point, например

using static System.Console;

WriteLine("Ok");

В Kotlin нужна функция main

fun main() = println("Ok")

По этим небольшим двум примерам в первую очередь можно заметить, что в Kotlin можно опускать точку с запятой. При более глубоком анализе видим, что в C#, несмотря на лаконичность показательного entry point, статические методы в остальных файлах по прежнему требуется оборачивать в класс и явно импортировать из него (using static System.Console), а Kotlin идет дальше и возвращает нас к top level функциям.

Обьявление переменных

В C# тип пишется слева, а для создания экземпляра используется ключевое слово new. В наличии есть специальное слово var, которым можно заменить имя типа слева. При этом переменные внутри методов в C# остаются подвержены повторному присваиванию.

Point y = new Point(0, 0); 
var x = new Point(1, 2);
x = y; // Нормально

В Kotlin типы пишутся справа, однако их можно опускать. Помимо var, доступен и val который не допускает повторного присваивания. При создании экземляров не нужно указывать new.

val y: Point = Point(0, 0)
val x = Point(1, 2)
x = y // Ошибка компиляции!

Работа с памятью

В C# нам доступны значимые (обычно размещаются на стеке) и ссылочные (обычно размещаются в куче) типы. Такая возможность позволяет применять низкоуровневые оптимизации и сокращать расход оперативной памяти. Для объектов структур и классов оператор '==' будет вести себя по разному, сравнивая значения или ссылки, впрочем это поведение можно изменить благодаря перегрузке. При этом на структуры накладываются некоторые ограничения связанные с наследованием.

struct ValueType {} // структура, экземпляры попадут на стек
class ReferenceType {} // ссылочный тип, экземпляры будут в куче

Что до Kotlin, то у него нет никакого разделения по работе с памятью. Сравнение '==' всегда происходит по значению, для сравнения по ссылке есть отдельный оператор '==='. Объекты практически всегда размещаются в куче, и только для некоторых базовых типов, например Int, Char, Double, компилятор может применить оптизмизации сделав их примитивами jvm и разместив на стеке, что никак не отражается на их семантике в синтаксисе. Складывается впечатление что рантайм и работа с памятью это более сильная сторона .NET в целом.

Свойства классов

В C# доступна удобная абстракция вместо методов get/set, то есть всем известные свойства. При этом традиционные поля остаются доступны.

class Example
{   
  // Вычислено заранее и сохранено в backing field
  public string Name1 { get; set; } = "Pre-calculated expression";
  
  // Вычисляется при каждом обращении
  public string Name2 => "Calculated now";
  
  // Традиционное поле
  private const string Name3 = "Field"; 
}

В Kotlin полей нет вообще, по умолчанию доступны только свойства. При этом в отличие от C# public это область видимости по умолчанию, поэтому это ключевое слово рекомендукется опускать. Для разницы между свойствами сетером и без используются все те же var/val

class Example(var name1: String = "Pre-calculated expression") {
  
  val name2 get() = "Calculated now"
}

Классы данных

В C# достаточно слова record чтобы создать класс для хранения данных, он будет обладать семантикой значимых типов в сравнении, однако по прежнему остается ссылочным (будет размещаться в куче):

record Person(string FirstName, string LastName);

... 
  
Person person1 = new("Nancy", "Davolio");
Person person2 = person1 with { FirstName = "John" };

В Kotlin нужно дописать ключевое слово data к слову class

data class Person(val firstName: String, val lastName: String)

...

val person1 = Person("Nancy", "Davolio")
val person2 = person1.copy(firstName = "John")

Методы-расширения типов

В C# такие типы должны находиться в отдельном статическом классе и принимать вызывающий первым аргументом, помеченным this

static class StringExt
{
  public static Println(this string s) => System.Console.WriteLine(s)
    
  public static Double(this string s) => s + s
}

В Kotlin расширямый тип должен находиться слева от метода, который можно разместить в любом месте. При этом расширить тип можно не только методом, но и свойством

fun String.println() = println(this)

fun String.double get() = this * 2

Лямбда выражения

В C# для них есть специальный оператор =>

numbers.Any(e => e % 2 == 0);
numbers.Any(e => 
  {
    // объемная логика ...
    return calculatedResult;
  })

В Kotlin лямбды органично вписываются в Си-подобный синтаксис, кроме того во многих случаях компилятор заинлайнит их вызовы прямо в используемый метод

numbers.any { it % 2 == 0 }
numbers.any {
  // объемная логика
  return calculatedResult
}

Условия и шаблоны

У C# есть очень мощный pattern matching c условиями (пример из документации)

static Point Transform(Point point) => point switch
{
  { X: 0, Y: 0 }                    => new Point(0, 0),
  { X: var x, Y: var y } when x < y => new Point(x + y, y),
  { X: var x, Y: var y } when x > y => new Point(x - y, y),
  { X: var x, Y: var y }            => new Point(2 * x, 2 * y),
};

У Kotlin есть аналогичное switch выражение when, которое, несмотря на наличие возможности сопоставления с образцом, не может одновременно содержать деконструкции и охранных условий, но благодаря лаконичному синтаксису можно выкрутиться:

fun transform(p: Point) = when(p) {
  Point(0, 0) -> Point(0, 0)
  else -> when {
    x > y     -> Point(...)
    x < y     -> Point(...)
    else      -> Point(...)
  }
}

// или так
fun transform(p: Point) = when {
  p == Point(0, 0) -> Point(0, 0)
  p.x < y          -> Point(p.x + y, p.y)
  p.x > y          -> Point(p.x - p.y, p.y)
  else             -> Point(2 * p.x, 2 * p.y)
}

Подводя итоги

Уложить в одной статье все отличия обоих языков практически нереально. Однако кое какие выводы сделать уже можем. Заметно что Kotlin-way скорее в том чтобы минимизировать количество ключевых слов, реализуя весь сахар на базвом синтаксисе и оставляя опциональность, а C# стремится стать более удобным увеличивая количество доступных выражений на уровне самого языка. У Kotlin преимущество в том что его создатели могли оглядываться на удачные фичи C# и лаконизировать их, а C# выигрывает за счет более мощной поддержки в лице Microsoft и лучшего рантайма .NET

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Я бы хотел начать новый проект на…

  • 0,0%C#0
  • 100,0%Kotlin2
Источник: https://habr.com/ru/post/560358/


Интересные статьи

Интересные статьи

Такой провокационный вопрос задал реддитор nenemen в сабреддите Java:«Я думаю о том, чтобы свой следующий проект сделать на Kotlin + Spring Boot, но мощь всенародной любв...
Доброго всем времени суток! С вами я, Анна Жаркова, ведущий мобильный разработчик компании «Usetech» Я давно занимаюсь не только нативной разработкой (как iOS, так и Android), но и крос...
Примечание от переводчиков: при словах «мобильные приложения Netflix» вспоминаются их основные приложения для просмотра видеоконтента, но компания ведёт и «киностудийную» разработку — для...
Этот пост будет из серии, об инструментах безопасности, которые доступны в Битриксе сразу «из коробки». Перечислю их все, скажу какой инструмент в какой редакции Битрикса доступен, кратко и не очень р...
Некоторое время назад мне довелось пройти больше десятка собеседований на позицию php-программиста (битрикс). К удивлению, требования в различных организациях отличаются совсем незначительно и...