Pull to refresh

Неявно типизированные поля в C#

Reading time4 min
Views11K
Сегодня на кывте был задан очередной весьма интересный вопрос о том, почему в языке C# существуют неявно типизированные локальные переменные (implicitely-typed local variables) a.k.a. var, но нет неявно типизированных полей?

На самом деле, такое положение дел вовсе не случайно; так что давайте рассмотрим несколько причин, почему компилятор ведет себя именно так, а не иначе.

Во-первых, возможность использовать var, для объявления неявно типизированных локальных переменных, никогда не была самостоятельной возможностью. При разработке языка C# никто не ставил перед собой цели создать полностью неявно типизированный язык программирования, типа F#; неявная типизация была лишь одной составляющей (пусть и немаловажной) более общей концепции, известной сегодня под аббревиатурой LINQ.

Поскольку при разрабоке LINQ-а было принято решение, что завязывать разработчика только на существующие типы является слишком «злым» ограничением, то при ее реализации была предоставлена разработчику возможность возвращать последовательности анонимных классов. А раз так, то без использования неявно типизированных переменных это было бы не возможно:

IEnumerable<Customer> customers = null;
// Упс, а тип переменной result какой? IEnumerable<??>?
var result = from c in customers
        where c.Age > 33
        select new {c.Name, c.Age};

* This source code was highlighted with Source Code Highlighter.


Однако использование ключевого слова var в объявлении типа никак бы не помогло решить более глобальную цель (это я опять про LINQ).

Во-вторых, даже если не залазить слишком глубоко в то, насколько сложно было бы реализовать такую возможность компиляторописателям (*), даже навскидку с ее реализацией и использованием связаны некоторые недостатки. Первый недостаток связан с тем, что неявно типизированные поля будут плохо дружить с анонимными типами.

Давайте представим себе следующий класс:

public class Foo
{
  public var someField = new {Name = "name", Value = 12};
}

* This source code was highlighted with Source Code Highlighter.


Поскольку анонимные типы в .Net реализованы сейчас в виде internal-типов, то «экспортировать» этот тип за пределы текущей сборки просто напросто нельзя. Можно, конечно же, ограничить использование неявно типизированных полей только для интернал классов или приватных полей, или вообще запретить использование неявно типизированных полей с анонимными классами, но это сделает поведение двух семантически схожих конструкций языка C# слишком разным. Одна из причин появление var в C# 3.0 – это использование анонимных типов (с LINQ-ом или без), а тут получается, что неявнотипизированные поля с ними работать не будут.

Еще одним важным ограничением является то, что неявно типизированные поля вводят слишком тесную связь между инициализируемым полем и «инициализатором». Давайте рассмотрим такой пример: предположим, у нас есть класс A, который содержит “var”-поле с именем foo, которое инициализируется путем вызова статического метода Foo класса B:

public class A
{
  public static var foo = B.Foo();
}

public class B
{
  public static int Foo()
  {
    return default(int);
  }
}

* This source code was highlighted with Source Code Highlighter.


Это приводит к целому ряду дополнительных вопросов: а что будет, если классы A и B расположены в разных сборках? А что если без перекомпиляции сборки A будет перекомпилирована сборка с классом B и тип возвращаемого значения метода Foo изменится с типа int на string? Или же у нас может быть еще неявно типизированное поле C.foo (т.е. поле foo в классе С), завязанное на тип поля A.foo, поле D.foo, завязанное на поле C.foo и т.д. и тогда изменение типа возвращаемого значения одной функции приведет к изменению типов полей в десятке разных классов (**). Да и вообще, поля являются более важной частью дизайна класса и его реализации по сравнению с локальными переменными, поэтому изменение «на лету» типа этого поля, только из-за того кто-то поменял сигнатуру функции в третьем модуле является не лучшей идеей.

Конечно же, можно было бы ограничить возможность использования неявно типизированных полей лишь в ограниченном наборе случаев, и запрещать использование этой возможности с анонимными типами и классами из других сборок, а оставить, например, только использование методов текущего класса. Но даже в этом случае реализация этой возможности требует слишком больших усилий на реализацию (пруф у Эрика, я тут не причемJ), что делает ее довольно низкоприоритетной в бесконечном списке улучшений, которые есть в головах разработчиков компилятора языка C#.

----------------------

(*) Залазить «в дебри» нет никакого смысла, поскольку это уже сделал Эрик Липперт в своей заметке Why no var on fields?, в которой он как раз и рассказывает о том, что реализация неявно типизированных полей потребовала бы значительно более существенных затрат на реализацию, нежели реализация неявно типизированных локальных переменных.

(**) Конечно, это не показатель, и любую возможность использовать правильно или не правильно. Но с помощью этой штуки можно нагородить такого, что пример, приведенный в заметке Как не надо писать код случай, может еще и цветочками показаться (поскольку мелкие изменения будут приводить к мессу не в одном классе, а еще и в десятке других).
Tags:
Hubs:
+20
Comments55

Articles