C# — есть ли что-то лишнее? |
||
МЕНЮ Искусственный интеллект Поиск Регистрация на сайте Помощь проекту Архив новостей ТЕМЫ Новости ИИ Искусственный интеллект Разработка ИИГолосовой помощник Городские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Техническое зрение Чат-боты Авторизация |
2016-06-19 10:35 Все будет быстро. Это выступление Анатолия Левенчука, в последнее время не дает мне покоя. Успехи глубинного обучения в последний год говорят о том, что все очень быстро изменится. Слишком долго
Почему же тогда существуют сложные языки? Все дело в выразительности. Если какая-то конструкция позволяет коротко описать необходимое действие, то это вполне может окупить негативные стороны усложнения языка. Лирические воспоминания C# 3.0 Implicitly typed local variables var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] {1, 2, 3}; var orders = new Dictionary<int,Order>(); Пресловутое var. О введении которого сейчас спорят в мире Java («Var и val в Java?», «Ключевое слово «var» в Java: пожалуйста, только не это») List<Pair<String, Double>> scores = seeker.getScores(documentAsCentroid); ... foreach(Pair<String, Double> score in scores) И это (Pair<String, Double>) далеко не самый длинное определение типа, которое приходится повторять. А любые повторы - это действительно плохо (помимо того, что просто неуклюже). Но есть способ значительно лучше и выразительнее. Вот чего мне после Паскаля не хватало в Java, а затем в C#, так это конструкции типа Type (typedef в C). В C# под это дело пытался приспособить using, который позволяет в начале файла написать что-то типа: using StopWordsTables = System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, string>>; Эта конструкция позволяла вместо той громоздкой писанины, что стоит справа, использовать идентификатор StopWordsTables. К сожалению, он действителен только в пределах одного исходного файла- Object and collection initializers var r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } }; List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Штука полезная, сокращающая код не в ущерб читаемости. Одобряю. Auto-Implemented properties Теперь, вместо public Class Point { private int x; private int y; public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } Стало возможно писать так: public Class Point { public int X { get; set; } public int Y { get; set; } } Насчет свойств в глубине души так и не понял, а нужно ли их было вводить? Вон, в той же Java и без них вполне нормально жить, используя определенные соглашения имен в методах. Но коль уж ввели, то такое упрощение их описания вполне удобно (без ухудшения читабельности) во многих случаях. Anonymous types var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2; Мне данная опция языка так ни разу и не пригодилась. Хотя нет, 1 раз таки нужно было, вспомнил. Я бы не вводил. Хотя те примеры, что видел в учебнике, вроде бы и логичны. В общем, возможно штука и полезная, просто не в моих сценариях (предпочитаю возиться с алгоритмами, а не с базами и JSON, хотя, разное бывает). Extension methods namespace Acme.Utilities { public static class Extensions { public static int ToInt32(this string s) { return Int32.Parse(s); } public static T[] Slice<T>(this T[] source, int index, int count) { if (index < 0 || count < 0 || source.Length - index < count) throw new ArgumentException(); T[] result = new T[count]; Array.Copy(source, index, result, 0, count); return result; } } } using Acme.Utilities; ... string s = "1234"; int i = s.ToInt32(); // Same as Extensions.ToInt32(s) int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int[] a = digits.Slice(4, 3); // Same as Extensions.Slice(digits, 4, 3) Очень удобная штука. Временами теоретики ООП её ругают, но без неё было бы неудобно (громоздко) делать многие вещи. Query expressions Он же LINQ. Этот пункт вызывает настолько смешанные чувства! Ну, примерно, как ложка дегтя в бочке чего-то хорошего. Вне всякого сомнения, LINQ явилась одной самых, по настоящему классных возможностей языка. Но зачем нужно было реализовывать это двумя способами? Я про так называемый человеко-понятный синтаксис (ЧПС), который имитировал SQL-запросы, насколько я понимаю. string[] people = new [] { "Tom", "Dick", "Harry" }; // ЧПС или же синтаксис запросов var filteredPeople = from p in people where p.Length > 3 select p; // функциональный стиль или лямбда синтаксис var filteredPeople = people.Where (p => p.Length > 3); В результате имеем:
Похожие чувства в плане чужеродности стиля у меня вызывают байндинги WPF. Они представляют собой микроскриптовые конструкции, написанные на своем языке внутри XML. В результате все выглядит громоздко и некрасиво. Не знаю, как можно было бы сделать красивее - может создать специализированный язык разметки, а не городить все в XML? Но я отвлекся. В общем, признаюсь - за последние несколько лет не написал ни одного выражения в ЧПС, при этом совершенно не испытывая в этом потребности. Только немножко редактировал чужие. Lambda expressions x => x + 1 // Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, statement body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, statement body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters Это было прекрасное приобретение, привнесшее в C# элементы функционального стиля и существенно улучшившего выразительность коротких фрагментов кода, передаваемых как аргументы. Вот только когда лямбды начинают занимать с десяток и более строк, читать код становится очень сложно. Важно вовремя остановиться и в этом случае перейти опять на методы. Expression trees Вряд ли стоит рассматривать данную фичу отдельно от LINQ и Lambda. Partial methods Неплохой способ разделить автоматически генерируемый и ручной код. Я - за. Веня, он же Бэн C# 4.0 Dynamic binding Потенциально полезный пример - вместо var doc = new XmlDocument("/path/to/file.xml"); var baz = doc.GetElement("foo").GetElement("qux"); можно написать dynamic doc = new XmlDocument("/path/to/file.xml"); var baz = doc.foo.qux; Несмотря на то, что выглядит хорошо, я бы не рекомендовал такое использование. Тип dynamic - очень опасная штука, поскольку теряется весь контроль типов. Из более мелких пакостей - перестают работать подсказки в редакторе. Тем не менее, в определенных сценариях, он полезен. Например, с его помощью я у себя делал подгрузку плагинов (точнее, использование кода из них). За счет того, что вызовы методов здесь кешируются, то получается производительно и не нужно городить это самостоятельно. А насчет безопасности - иначе мне бы все-равно пришлось бы работать через рефлексию, так что в этом случае безопасность не была бы большей. А вот код был бы более сложным и запутанным. Так что осторожное использование динамиков в ограниченном количестве сценариев одобряю. Конечно, вводились они больше с прицелом на скриптовые языки. Ну, нужно, так нужно. Named and optional arguments class Car { public void Accelerate( double speed, int? gear = null, bool inReverse = false) { /* ... */ } } Car myCar = new Car(); myCar.Accelerate(55); Уменьшается количество перегруженных методов - код становится проще и надежнее (меньше возможностей совершить ошибку копипаста, меньше работы при рефакторинге). Одобряю. Generic co- and contravariance Вполне логичное уточнение поведения языка. Особой сложности в изучение и синтаксис не вносит и может быть рассмотрено новичками позже. Одобряю. Embedded interop types («NoPIA») Это одна из фич, про которые мне особо нечего сказать, исходя из своей практики - просто читал, что такое есть. Мне она не нужна была, но COM видимо еще долго не умрет и тем, кто (например) работает с MS Office, он еще долго будет нужен. C# 5.0 Asynchronous methods public async Task ReadFirstBytesAsync(string filePath1, string filePath2) { using (FileStream fs1 = new FileStream(filePath1, FileMode.Open)) using (FileStream fs2 = new FileStream(filePath2, FileMode.Open)) { await fs1.ReadAsync(new byte[1], 0, 1); // 1 await fs2.ReadAsync(new byte[1], 0, 1); // 2 } } Очень удобная конструкция. К сожалению, при практической реализации возникли некоторые ограничения - детали реализации протекали в виде ограничений (Async/await в C#: подводные камни). Часть их была снята в следующих версиях (Await in catch/finally blocks) языка, или библиотек (akka.net поначалу не позволяла смешивать свою модель асинхронного исполнения с рассматриваемой фичей, но потом это поправили). Может быть имело бы смысл рассмотреть и какие-то другие паттерны параллельного взаимодействия - типа горутин. Но тут уже выбор за архитекторами языка. В общем, одобряю. Caller info attributes public void DoProcessing() { TraceMessage("Something happened."); } public void TraceMessage(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) { System.Diagnostics.Trace.WriteLine("message: " + message); System.Diagnostics.Trace.WriteLine("member name: " + memberName); System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath); System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber); } // Sample Output: // message: Something happened. // member name: DoProcessing // source file path: c:UsersusernameDocumentsVisual Studio 2012ProjectsCallerInfoCSCallerInfoCSForm1.cs // source line number: 31 Небольшой синтаксический сахар, который не утяжеляет язык, но позволяет в определенных сценариях уменьшить количество копипаста и кода. Одобряю. C# 6.0 Compiler-as-a-service (Roslyn) Этот пункт (несмотря на общую важность) пропущу. Я бы отнес его скорее к инфраструктуре, а не непосредственно к языку. Import of static type members into namespace using static System.Console; using static System.Math; using static System.DayOfWeek; class Program { static void Main() { WriteLine(Sqrt(3*3 + 4*4)); WriteLine(Friday - Monday); } } Поначалу мне эта фича показалась полезной. Но попробовав её на практике, вынужден признать, что ошибся. Читаемость кода ухудшается - методы и члены статического класса начинают мешаться с методами текущего класса. И даже ввод стал медленнее, хотя вроде бы количество идентификаторов уменьшилось на единицу. Но за счет того, что теперь в подсказке от Intellisense больше вариантов, нажатий нужно сделать больше. В общем, данная фича, с моей точки зрения - ошибка. Exception filters try { - } catch (MyException e) when (myfilter(e)) { - } Еще не попробовал. Поэтому есть искушение назвать фичу бесполезной, но может просто мои сценарии к ней не сильно подходят? Может, кто расскажет, в каких случаях и насколько часто она реально хороша? Await in catch/finally blocks Не считаю это самостоятельной фичей - скорее исправление предыдущих проблем. Auto property initializers public class Customer { public string First { get; set; } = "Jane"; public string Last { get; set; } = "Doe"; } Логичное и удобное дополнение к автосвойствам. Код становится чище, а значит одобряю. Default values for getter-only properties public class Customer { public string First { get; } = "Jane"; public string Last { get; } = "Doe"; } Аналогично предыдущему пункту. Expression-bodied members public string Name => First + " " + Last; public Customer this[long id] => store.LookupCustomer(id); Большой практики применения пока нет, но выглядит неплохо. Нужно будет еще пройтись по своему коду и посмотреть, где можно бы применить. Главное здесь как с лямбдами - не переусердствовать и не делать выражений на полэкрана. Null propagator (succinct null checking) public static string Truncate(string value, int length) { return value?.Substring(0, Math.Min(value.Length, length)); } Давно напрашивавшаяся штука. Одобряю. Хотя, на практике и оказалось, что применяется не так часто, как ожидалось до того. String Interpolation О! Вот это то, чего ждал давным-давно, и что мгновенно прижилось в моем коде. Всегда старался писать идентификаторы в контексте строки примерно так: -Total lines: - + totalLines + -, total words: - + totalWords + -.-; Иногда меня спрашивали, а знаю ли я про форматирование строк? Да, знаю, но там есть 2 большие проблемы:
Также это приводит к тому, что в методах Format(...) допускается большое количество ошибок при рефакторинге. nameof operator if (x == null) throw new ArgumentNullException(nameof(x)); WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode" Аналогично -Caller info attributes-. Одобряю. Dictionary initializer var numbers = new Dictionary<int, string> { [7] = "seven", [9] = "nine", [13] = "thirteen" }; Еще одно новшество, которое позволяет упростить работу с кодом, который нередко набирается с помощью копипаста и подвержен ошибкам от невнимательности. Любая возможность сделать его чище и удобнее для чтения будет приводить к плюсам в карме архитектора языка. Плюсик. Анти-Бэн C# 7.0 proposals Данными функциями я еще не пользовался - обычно сижу на релизной версии шарпа, иногда приходится спускаться чуть ниже. Поэтому здесь приведу чисто умозрительные аргументы. Список приведу по статье -Новшества C# 7-, а не по данным из википедии. Binary literals int x = 0b1010000; int SeventyFive = 0B10_01011; Новшество выглядит безобидно, не сильно усложняя язык, а для тех, кому нужно работать с битами - удобно. Немного не понял фразу «Можно отделять нули произвольным количеством подчёркиваний» - почему только нули? Local functions public void Foo(int z) { void Init() { Boo = z; } Init(); } Когда только перешел на C# с Объектного Паскаля (Delphi), мне очень не хватало локальных функций, как способа структурировать свой код. Простое вынесение кусков кода в приватные методы приводило к появлению классов с большим количеством методов на одном уровне. Так происходило, пока я не понял, что в C# для этого нужно применять другой метод - объектную декомпозицию. После этого я часто стал выносить относительно громоздкий код во внутренний класс со своими методами. По достижении определенного уровня сложности, этот класс мог быть разделен на несколько связанных классов, которые выносились в отдельную папку и нэймспейс. Это позволило привнести ту иерархию в код, которую в стародавние времена обеспечивали локальные функции и процедуры Паскаля. Tuples Пока не понял необходимости данной фичи, в каких ситуациях она будет полезнее, чем вернуть класс/структуру или же использовать out-аргументы. Поэтому для меня это скорее отрицательный вклад в язык. Pattern matching, conditions in switch // type pattern public void Foo(object item) { if (item is string s) { WriteLine(s.Length); } } // Var Pattern public void Foo(object item) { if(item is var x) { WriteLine(item == x); // prints true } } // Wildcard Pattern public void Foo(object item) { if(item is *) { WriteLine("Hi there"); //will be executed } } Более полный пример можно посмотреть по ссылке. Ref locals and ref returns static void Main() { var arr = new[] { 1, 2, 3, 4 }; ref int Get(int[] array, int index)=> ref array[index]; ref int item = ref Get(arr, 1); WriteLine(item); item = 10; WriteLine(arr[1]); ReadLine(); } // Will print // 2 // 10 Простое и интуитивно понятное расширение для работы со ссылками. Но реальная нужда в нем пока непонятна. Итак, что в итоге? С моей точки зрения, C# нажил (7-ю версию пока не трогаю, оплакивать буду по факту) себе такие лишние фичи:
Что было особенно полезно:
Источник: habrahabr.ru Комментарии: |
|