Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

123 Commits
 
 
 
 

Repository files navigation

История языка C#

Нововведения в разных версиях языка C#. Текст сгенерирован с помощью DeepSeek.
После чего был исправлен и дополнен ссылками и примерами.
Подробнее можно прочитать у профильных блогеров: блогер 1, блогер 2, блогер 3, блогер 4, блогер 5.
Также есть похожая статья на сайте Микрософт, на сайте Википедии, ответ в Stack Overflow, вехи в github.
Для практики можно воспользоваться бесплатными курсами от Микрософт.

От себя добавлю, что в проекте можно менять версию .NET, но нельзя менять версию языка C#.
Скажем, Visual Studio 2019 (2022) выставляет версию языка в 7.3 для классического .NET Framework любой версии.
Visual Studio 2022 для .NET Core 3, 5, 6, 7, 8, 9 выставляет версию языка 8, 9, 10, 11, 12, 13 соотвественно.
Уточнить версию компилятора можно вставив в исходники строку #error version.
В статье я рассматриваю только изменения синтаксиса языка, нововведения в стандартной библиотеке не упоминаются.


C# 1.0 (январь 2002)

Пример кода C# 1.0
using System;

namespace CSharp_1
{
    interface IShape
    {
        string Name { get; }
        double CalcArea();
        double CalcPerimeter();
        string Info();
    }

    class Rectangle : IShape
    {
        double _width, _height;
        public Rectangle(double width, double height)
        {
            if (width <= 0 || height <= 0)
                throw new ArgumentException("Сторона должна быть больше 0");
            _width = width;
            _height = height;
        }
        public string Name { get { return "Прямоугольник"; } }

        public double CalcArea()
        {
            return _width * _height;
        }

        public double CalcPerimeter()
        {
            return 2 * (_width + _height);
        }

        public string Info()
        {
            return string.Format("ширина: {0:0.##}, высота: {1:0.##}", _width, _height);
        }
    }

    class Circle : IShape
    {
        double _radius;
        public Circle(double radius)
        {
            _radius = radius;
        }
        public string Name { get { return "Круг"; } }

        public double CalcArea()
        {
            return Math.PI * _radius * _radius;
        }

        public double CalcPerimeter()
        {
            return 2 * Math.PI * _radius;
        }

        public string Info()
        {
            return string.Format("радиус: {0:0.##}", _radius);
        }
    }

    delegate void ShapeEventHandler(string message);
    class ShapeManager
    {
        public event ShapeEventHandler ShapeCreated;

        public void CreateShape(IShape shape)
        {
            if (ShapeCreated != null)
            {
                ShapeCreated(string.Format(
                    "Создана фигура: {0}, {1}, периметр: {2:0.##}, площадь: {3:0.##}", 
                    shape.Name, shape.Info(), shape.CalcPerimeter(), shape.CalcArea()
                ));
            }
        }
    }

    class Program
    {
        static void ShowInfo(string message)
        {
            Console.WriteLine(message);
        }

        public static void Main()
        {
            try
            {
                ShapeManager sm = new ShapeManager();
                sm.ShapeCreated += new ShapeEventHandler(ShowInfo);

                Rectangle rect = new Rectangle(10, 20);
                sm.CreateShape(rect);

                Circle circle = new Circle(10.1010);
                sm.CreateShape(circle);

                Rectangle invalidRect = new Rectangle(-20, -30);
                sm.CreateShape(invalidRect);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine("Ошибка: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Неизвестная ошибка: " + ex.ToString());
            }
        }
    }
}

C# 1.2 (апрель 2003)

  • Основные нововведения:
    • foreach вызывает Dispose
    • Исправления и оптимизация
  • Версия .NET: .NET Framework 1.1.
  • Версия Visual Studio: Visual Studio .NET 2003.
Пример кода C# 1.2
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

class Range : IEnumerable, IEnumerator, IDisposable
{
    int[] _data;
    int _pos = -1;

    public Range(int start, int end)
    {
        if (end < start)
            throw new Exception("Invalid range");
        _data = new int[end - start];
        for (int i = 0; i < end - start; i++)
            _data[i] = i + start;
    }
    public object Current { get { return _data[_pos]; } }

    public void Dispose()
    {
        Console.WriteLine("Dispose"); //not called in c# 1.0
    }

    public IEnumerator GetEnumerator()
    {
        this.Reset();
        return this;
    }

    public bool MoveNext()
    {
        _pos++;
        return _pos < _data.Length;
    }

    public void Reset()
    {
        _pos = -1;
    }
}

class Program
{
    static void Main(string[] args)
    {
        IEnumerable r = new Range(1, 3);
        foreach (var i in r)
            Console.WriteLine(i);
        foreach (var i in r)
            Console.WriteLine(i);
    }
}

C# 2.0 (ноябрь 2005)

Пример кода C# 2.0
using System;
using System.Collections.Generic;

namespace CSharp_2
{
    delegate void DoAction<T>(T item);

    static class CollectionUtils
    {
        public static List<T> CreateList<T>(params T[] items)
        {
            return new List<T>(items);
        }

        public static IEnumerable<T> Reverse<T>(IEnumerable<T> items)
        {
            List<T> buffer = new List<T>(items);
            for (int i = buffer.Count - 1; i >= 0; i--)
                yield return buffer[i];
        }

        public static void ForEach<T>(IEnumerable<T> items, DoAction<T> action, bool reverse)
        {
            if (reverse)
                items = Reverse(items);
            foreach (T item in items)
                action(item);
        }
    }

    class Program
    {
        public static void Main()
        {
            List<int?> list = CollectionUtils.CreateList<int?>(1, 2, null, 4, 5);
            CollectionUtils.ForEach(list, delegate (int? i)
            {
                Console.WriteLine(i * i ?? default(int));
            }, true);
        }
    }
}

C# 3.0 (ноябрь 2007)

Пример кода C# 3.0
using System;
using System.Collections.Generic;
using System.Linq;

namespace CSharp_3
{
    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    static class PersonExtension
    {
        public static bool IsAdult(this Person p)
        {
            return p.Age >= 18;
        }
    }

    class Program
    {
        public static void Main()
        {
            var list = new List<Person>
            {
                new Person { Name = "Alex", Age = 10},
                new Person { Name = "Ivan", Age = 20},
                new Person { Name = "Peter", Age = 30}
            };

            var filtered = list.Where(p => p.IsAdult()).Select(p => new { p.Name });
            foreach (var p in filtered)
                Console.WriteLine(p.Name); 
        }
    }
}

C# 4.0 (апрель 2010)

  • Основные нововведения:
  • Версия .NET: .NET Framework 4.0.
  • Версия Visual Studio: Visual Studio 2010.
Пример кода C# 4.0
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp13
{
    class Program
    {
        class Bot
        {
            public void Hello(string prefix = "") { Console.WriteLine(prefix + "Hello"); }
            public void Bye(string prefix = "") { Console.WriteLine(prefix + "Bye"); }
        }

        static void Main(string[] args)
        {
            var bots = new Bot[5];
            for (int i = 0; i < bots.Length; i++)
                bots[i] = new Bot();
            Parallel.For(0, bots.Length, i => Print(bots[i], num: i, delay: 100));
        }

        static void Print(dynamic obj, int num, int delay = 10)
        {
            obj.Hello("Bot " + num + " say: ");
            Thread.Sleep(delay);
            obj.Bye("Bot " + num + " say: ");
        }

    }
}

C# 5.0 (август 2012)

  • Основные нововведения:
  • Версия .NET: .NET Framework 4.5.
  • Версия Visual Studio: Visual Studio 2012.
Пример кода C# 5.0
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

public enum LogLevel { Debug, Info, Warning, Error }

public static class Logger
{
    public static void Log(
        string message,
        LogLevel level = LogLevel.Info,
        [CallerMemberName] string callerName = "",
        [CallerFilePath] string callerFilePath = "",
        [CallerLineNumber] int callerLineNumber = 0)
    {
        string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        string fileName = System.IO.Path.GetFileName(callerFilePath);
        string levelStr = level.ToString().ToUpper();

        Console.WriteLine($"[{timestamp}] [{levelStr}] [{callerName}() in {fileName}:{callerLineNumber}] {message}");
    }
}

public static class ConsoleApplication
{
    public static void Main()
    {
        Logger.Log("Начало программы", LogLevel.Debug);
        MethodAsync().GetAwaiter().GetResult();
    }

    private static async Task MethodAsync()
    {
        Logger.Log(">MethodAsync");
        await Task.Delay(100);
        Logger.Log("<MethodAsync");
    }
}

C# 6.0 (июль 2015)

подробнее с примерами

Пример кода C# 6.0
using System;
using static System.Console;

public class Person
{
    public string Name { get; } = "Anonymous";
    public int Age { get; set; } = 18;

    public Person(string name) => Name = name;

    public override string ToString() => $"Name: {Name}, Age: {Age}";
}

public class Program
{
    public static void Main()
    {
        try
        {
            var person = new Person("Alice");
            person.Age = -1;
            WriteLine(person);        // "Name: Alice, Age: -1"

            // Проверка nameof (используется для безопасного получения имени переменной)
            ValidatePerson(person);
        }
        catch (Exception ex) when (ex.Message.Contains("Age")) // Фильтр исключений
        {
            WriteLine($"Проблема с возрастом: {ex.Message}");
        }
        catch (Exception ex)
        {
            WriteLine($"Ошибка: {ex.Message}");
        }
    }

    // Метод с использованием nameof для валидации
    public static void ValidatePerson(Person person)
    {
        if (person == null)
            throw new ArgumentNullException(nameof(person)); // Безопасное имя параметра

        if (person.Age < 0)
            throw new ArgumentException("Age cannot be negative", nameof(person.Age));
    }
}

C# 7.0 (март 2017)

подробнее с примерами, серия статей

Пример кода C# 7.0
using System;

class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

class Program
{
    static void PrintPoints(Point[] points)
    {
        foreach (var p in points)
            PrintPoint(p);

        void PrintPoint(Point p) => Console.WriteLine(p.X + " " + p.Y);
    }

    static ref Point GetPoint(Point[] points)
    {
        return ref points[0];
    }

    static void Main()
    {
        int.TryParse("1", out int val);
        Point[] points = { new Point(val, 2_0), new Point(3, 4) };
        ref var p = ref GetPoint(points);
        p = new Point(5, 6);
        var (x, y) = p;
        PrintPoints(points);

        switch (points[0])
        {
            case Point point when point.X == x:
                Console.WriteLine("Test ok");
                break;
        }
    }
}

C# 7.1 (август 2017)

подробнее с примерами

Пример кода C# 7.1
using System;
using System.Threading.Tasks;

class Program
{
    [Obsolete(message: "Этот метод устарел")]
    static async Task Main()
    {
        int count = 1000;
        string msg = default;
        var t = (count, msg);
        await Task.Delay(t.count);
    }
}

C# 7.2 (декабрь 2017)

подробнее с примерами

Пример кода C# 7.2
using System;

readonly struct Point
{
    public Point(int x, int y) => (X, Y) = (x, y);
    public int X { get; }
    public int Y { get; }
    public override string ToString() => $"({X}, {Y})";
    public double DistanceTo(Point p) => Math.Sqrt(Math.Pow(X - p.X, 2) + Math.Pow(Y - p.Y, 2));
}

class Program
{
    static void Main(string[] args)
    {
        ref readonly Point GetClosest(in Point t, in Point a, in Point b) =>
            ref (t.DistanceTo(a) < t.DistanceTo(b) ? ref a : ref b);

        Point target = new Point(1, -1);
        Point[] points = new Point[] { new Point(2, 3), new Point(3, 2) };
        ref readonly var closest = ref GetClosest(t: target, points[0], b: points[1]);

        Console.WriteLine($"Точка {target}");
        Console.WriteLine($"Массив точек: {String.Join(", ", points)}");
        Console.WriteLine($"Ближайшая точка в массиве: {closest}");
    }
}

C# 7.3 (май 2018)

подробнее с примерами: часть 1, часть 2

Пример кода C# 7.3
using System;

public class Example
{
    public static void Main()
    {
        int x = 5, y = 10;
        ref int r = ref x;
        r = ref y;
        r = 42;
        var tuple = (x, y);
        Console.WriteLine(tuple == (5, 42));
    }
}

C# 8.0 (сентябрь 2019)

подробнее с примерами: серия статей

Пример кода C# 8.0
#nullable enable

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

internal class Program
{
    static async Task Main(string[] args)
    {
        int[]? arr = null;
        var list = new List<int>();
        await foreach (var i in GetIntAsync())
            list.Add(i);
        arr ??= list.ToArray()[2..7];

        ProcessRange(null, arr);
        ProcessRange("null", null);
        ProcessRange("empty", Array.Empty<int>());

        static async IAsyncEnumerable<int> GetIntAsync()
        {
            for (int i = 0; i < 10; i++)
                yield return i;
        }

        static void ProcessRange(string? title, int[]? range)
        {
            var mess = range switch
            {
                null => "null",
                _ when range.Length == 0 => "empty",
                _ => string.Join(", ", range)
            };
            Console.WriteLine(@$"{title ?? "range"}: {mess}");
        }
    }
}

C# 9.0 (ноябрь 2020)

подробнее с примерами: серия статей

Пример кода C# 9.0
using System;
using System.Linq;
using System.Net.Http;
using System.Text.Json;

var client = new HttpClient();
var data = await client.GetStringAsync("https://jsonplaceholder.typicode.com/posts");
var posts = JsonSerializer.Deserialize<Post[]>(data)!;
var firstPost = posts.Where(p => p is { id:  1 }).First();
Console.WriteLine(firstPost);
var copyPost = firstPost with { title = "New title" };
copyPost.body = "New body";
Console.WriteLine(copyPost);

record Post (int id, int userId, string title)
{
    public string body { get; set; }

    public override string ToString() =>
        $"id: {id}, userId: {userId}, title: {Trunc(title, 20)}, body: {Trunc(body?.Replace('\n', ' '), 20)}";

    string Trunc(string str, int len) =>
        str?.Length > len ? str[..len] + "..." : str;
}

C# 10.0 (ноябрь 2021)

подробнее с примерами

Пример кода C# 10.0
global using System;
namespace CSharp10Sample;

readonly record struct Number(int value)
{
    public override string ToString() => value.ToString();
}

class Program
{
    static void Main()
    {
        var n = new Number();
        n = n with { value = 10 };
        var square = Number (Number n) => new Number(n.value * n.value);
        const string title = $"Just for information";
        Console.WriteLine($"{title}: {n} * {n} = {square(n)}");
    }
}

C# 11.0 (ноябрь 2022)

подробнее с примерами, на сайте микрософт

Пример кода C# 11.0
var container = ContainerString.Create("""Apple, Cherry""");
var items = container.List switch
{
    ["Apple, Plum"] => "Apple and Plum",
    [.., "Cherry"] => "Last Cherry",
    _ => "Unknown container"
};
Console.WriteLine(items);

file interface IContainer<T> where T: IContainer<T>
{
    static abstract T Create(string data);
}

file class ContainerString : IContainer<ContainerString>
{
    public required List<string> List { get; init; }
    public static ContainerString Create(string data) => new()
    {
        List = data
            .Split(",")
            .Select(s => s.Trim())
            .ToList()
    };
}

C# 12.0 (ноябрь 2023)

подробнее с примерами, на сайте микрософт

Пример кода C# 12.0
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using IntContainerInfo = (int Count, int Sum);

IntContainer data = [1, 2, 3];
Span<int> add = stackalloc int[] { 4, 1 };
add[0] = 5;
data = data.Concat(ref add).Exclude(1);
Console.WriteLine($"Content: {data}, Info: {data.Info}");

[CollectionBuilder(typeof(IntContainer), "Create")]
public class IntContainer(ReadOnlySpan<int> items): IEnumerable<int>
{
    private readonly int[] _items = items.ToArray();
    public static IntContainer Create(ReadOnlySpan<int> items) => new(items);
    public IntContainer Concat(ref readonly Span<int> items) => new([.. _items, .. items]);
    public IntContainer Exclude(int v = 0) => new(_items.Where(i => i != v).ToArray());
    public IntContainerInfo Info => (_items.Count(), _items.Sum());
    public override string ToString() => $"[{string.Join(", ", _items)}]";
    public IEnumerator<int> GetEnumerator() => _items.AsEnumerable().GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

C# 13.0 (ноябрь 2024)

подробнее с примерами, на сайте микрософт

Пример кода C# 13.0
using System;
using System.Runtime.CompilerServices;

class Program
{
    partial class ExtendableArray<T>
    {
        int _count;
        T[] _data;

        public ExtendableArray(int capacity = 0) => _data = new T[_count = capacity];

        [OverloadResolutionPriority(1)]
        public partial T this[Index i] { get; set; }
        public T this[int i] => _data[i];

        public override string ToString() => $"[{string.Join(", ", new ArraySegment<T>(_data, 0, _count))}]";
    }

    partial class ExtendableArray<T>
    {

        public partial T this[Index i]
        {
            get
            {
                CheckSize(i.GetOffset(_data.Length) + 1);
                return _data[i];
            }
            set
            {
                CheckSize(i.GetOffset(_data.Length) + 1);
                _data[i] = value;
            }
        }

        private void CheckSize(int size)
        {
            _count = Math.Max(_count, size);
            
            bool skip = _data.Length >= size;
            string start = skip ? "\e[32m" : "\e[31m";
            string end = "\e[0m";
            Console.WriteLine($"CheckSize {size}, skip={start}{skip}{end}");
            if (skip)
                return;
            
            int capacity = _data.Length == 0 ? 1 : _data.Length;
            while (capacity < size)
                capacity *= 2;

            var data = _data;
            _data = new T[capacity];
            data.CopyTo(_data, 0);
        }
    }

    public static void Main()
    {
        var list = new ExtendableArray<int>(3)
        {
            [0] = 1,
            [^1] = 2
        };
        list[3] = 3;
        list[4] = 1 + list[3];
        Console.WriteLine(list);
    }
}

C# 14.0 Preview (релиз ожидается в конце 2025)

на сайте микрософт

hits

About

Нововведения в разных версиях языка C#

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages