Многогранный Шерлок Холмс и Эраст Фандорин, идеальный аристократ, очень чтили дедуктивный метод и оба достигли в его применении потрясающих успехов. «Отбросьте все невозможное, то, что останется, и будет ответом, каким бы невероятным он ни казался» — так говорил сэр Артур Конан Дойль устами своего героя.
Однако же, на дедуктивных умозаключениях наука о рассуждениях не оканчивается — не стоит забывать еще и о индукции. О ней и о приближенных материях и будет трактовать сей трактат.
Индукция — это процесс логических умозаключений, основывающийся на переходе от частных положений к общему. Известный «тест на утку» является ярким примером подобного процесса:
Применительно к языкам программирования — в частности, к объектно-ориентированным — «тест на утку» приобретает особый смысл. В динамически типизированных языках семантика использования того или иной объекта определяется текущим набором его методов и свойств. К примеру, в статически типизированном языке, не поддерживающем утипизацию, можно создать функцию, принимающую в качестве параметра объект класса
Вкратце объяснив, что же такое утипизация и почему она недоступна в статически типизированных языках, я сейчас слегка опровергну сам себя. Для кого-то, возможно, это не будет откровением, но тем не менее. Некое подобие утиной типизации есть и в C#. Вот об этом сейчас поподробней.
Синтаксический сахар может быть чуть слаще, чем кажется на первый взгляд. В частности, это касается оператора
Если открыть C# Language Specification и заглянуть в раздел 8.8.4, то можно увидеть следующее:
Таким образом, следующий код прекрасно скомпилируется и выведет на консоль то, что ему положено:
Мелочь, я приятно.
Довольно приятное нововведение C# 3.0. Напомню:
Все бы ничего, но осталось определить термин «Collection». И вот с этим возникли проблемы.
Интерфейс
Казалось бы, решение найдено: любой класс, реализующий
Сдаваться в C# Design Group никто не думал. Было решено использовать уже хорошо зарекомендовавший себя Pattern-based syntax — а именно, вызывать метод
Пункт «наличие хотя бы одного...» требует небольшого пояснения. Заключенный в фигурные скобки список элементов не является «списком элементов, которые надо добавить» — это «список аргументов для методов
вполне легальным будет следующий инициализатор:
Поскольку разрешение перегрузок выполняется отдельно для каждого элемента из списка инициализации выполняется отдельно, то будут задействованы все три метода.
Шерлок Холмс, Эраст Петрович и ваш покорный слуга раскланиваются. Надеюсь, было интересно.
Спасибо Dr. T за пищу для размышлений.
Однако же, на дедуктивных умозаключениях наука о рассуждениях не оканчивается — не стоит забывать еще и о индукции. О ней и о приближенных материях и будет трактовать сей трактат.
Тест на утку
Индукция — это процесс логических умозаключений, основывающийся на переходе от частных положений к общему. Известный «тест на утку» является ярким примером подобного процесса:
If it walks like a duck and quacks like a duck, it must be a duck
(Если что-то ходит, как утка, и крякает, как утка, то это утка)
Применительно к языкам программирования — в частности, к объектно-ориентированным — «тест на утку» приобретает особый смысл. В динамически типизированных языках семантика использования того или иной объекта определяется текущим набором его методов и свойств. К примеру, в статически типизированном языке, не поддерживающем утипизацию, можно создать функцию, принимающую в качестве параметра объект класса
Duck
и вызывающую методы Quack
и Walk
этого объекта. В утипизированном же (и, как следствие, в динамически типизированном) языке аналогичная функция может принимать в качестве параметра некий абстрактный объект и попытаться вызывать у него те же методы (возможно, проверив их наличие). В статически типизированном языке подобного поведения можно достигнуть с использованием наследования и реализации интерфейсов, но это будет выражением формы is-a, в отличие от has-a в динамических языках.Вкратце объяснив, что же такое утипизация и почему она недоступна в статически типизированных языках, я сейчас слегка опровергну сам себя. Для кого-то, возможно, это не будет откровением, но тем не менее. Некое подобие утиной типизации есть и в C#. Вот об этом сейчас поподробней.
foreach
Синтаксический сахар может быть чуть слаще, чем кажется на первый взгляд. В частности, это касается оператора
foreach
. В большинстве учебников сказано, что для поддержки семантики foreach
в классе должен быть реализован (явно или неявно) интерфейс System.Collections.IEnumerable
. На деле же это не является до конца верным утверждением — если говорить о C#, то упомянутый интерфейс реализовывать совершенно необязательно.Если открыть C# Language Specification и заглянуть в раздел 8.8.4, то можно увидеть следующее:
The type of the expression of a foreach statement must be a collection type (as defined below), and an explicit conversion (§6.2) must exist from the element type of the collection to the type of the iteration variable. If expression has the valuenull
, aSystem.NullReferenceException
is thrown.
A typeC
is said to be a collection type if it implements theSystem.Collections.IEnumerable
interface or implements the collection pattern by meeting all of the following criteria:
C
contains a public instance method with the signatureGetEnumerator()
that returns a struct-type, class-type, or interface-type, which is calledE
in the following text.E
contains a public instance method with the signatureMoveNext()
and the return typebool
.- E contains a public instance property named
Current
that permits reading the current value. The type of this property is said to be the element type of the collection type.
Таким образом, следующий код прекрасно скомпилируется и выведет на консоль то, что ему положено:
using System;
using System.Collections.Generic;
namespace DuckTyping
{
class Collection
{
public Enumerator GetEnumerator()
{
return new Enumerator();
}
}
class Enumerator
{
private bool moveNext = true;
public object Current
{
get { return "Enumerator.Current"; }
}
public bool MoveNext()
{
if(!moveNext)
return false;
moveNext = false;
return true;
}
}
class Program
{
static void Main()
{
foreach(object o in new Collection())
Console.WriteLine(o);
}
}
}
* This source code was highlighted with Source Code Highlighter.
Мелочь, я приятно.
Collection Initializers
Довольно приятное нововведение C# 3.0. Напомню:
var digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
* This source code was highlighted with Source Code Highlighter.
Все бы ничего, но осталось определить термин «Collection». И вот с этим возникли проблемы.
Интерфейс
System.Collections.ICollection
до .NET 2.0 был совершенно бестолковым созданием. В 2.0 его починили: System.Collections.Generic.ICollection<T>
позволяет добавлять и удалять элементы, проходиться по ним упомянутым уже foreach
и узнавать число элементов в коллекции.Казалось бы, решение найдено: любой класс, реализующий
ICollection<T>
и будет называться коллекцией. Но не тут-то было — его практически никто не реализует. Из тысяч классов .NET BCL всего лишь несколько десятков могут похвастаться тем, что у них есть общедоступный конструктор без параметров и их можно привести к ICollection<T>
.Сдаваться в C# Design Group никто не думал. Было решено использовать уже хорошо зарекомендовавший себя Pattern-based syntax — а именно, вызывать метод
Add()
, если оный вообще есть. Теперь требования к коллекции с точки зрения Collection Initializer следующие: реализация IEnumerable
и наличие хотя бы одного общедоступного метода Add()
.Пункт «наличие хотя бы одного...» требует небольшого пояснения. Заключенный в фигурные скобки список элементов не является «списком элементов, которые надо добавить» — это «список аргументов для методов
Add()
». Список аргументов, таким образом, может быть гетерогенным. Другими словами, имея такой класс:public class Plurals : IDictionary<string,string>
{
public void Add(string singular, string plural);
public void Add(string singular);
public void Add(KeyValuePair<string,string> pair);
}
* This source code was highlighted with Source Code Highlighter.
вполне легальным будет следующий инициализатор:
var myPlurals = new Plurals{ “collection”, { “query”, “queries” }, new KeyValuePair(“child”, “children”) };
* This source code was highlighted with Source Code Highlighter.
Поскольку разрешение перегрузок выполняется отдельно для каждого элемента из списка инициализации выполняется отдельно, то будут задействованы все три метода.
Вместо заключения
Шерлок Холмс, Эраст Петрович и ваш покорный слуга раскланиваются. Надеюсь, было интересно.
Спасибо Dr. T за пищу для размышлений.