Вот я до него добрался и делюсь впечатлениями.
В общем, неплохо.
1) Nullable классы, например:
int? x = null;
x = 20;
int y = x ?? -1;
Оператор ?? разворачивается в
int y = x != null ? x : -1;
2) Параметризация классов. Здесь ничего нового по сравнению с C++.
class X<T>
{
private T y;
...
}
Параметры классов можно типизировать:
class X<T> where T : Y
{
private T y;
...
}
3) Разделяемые классы - подобно пространствам имён
partial class X
{
public int a;
}
partial class X
{
public int b;
}
...
X x = new X();
x.a = 1;
x.b = 2;
Части класса могут быть раскиданы по разным файлам. По-моему, такое новшество может быть использовано как во благо (в случае реализации множества интерфейсов в одном классе), так и во зло (когда программные модули раскиданы неизвестно где).
Вот что вызывает противоречивые чувства, так это конструкции yield.
Операторы "yield return" и "yield break" используются для простой реализации обхода данных в цикле foreach. Написал для себя пример обхода несбалансированного бинарного дерева.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Test
{
class Program
{
static void Main()
{
Random random = new Random(
Convert.ToInt32(
(Convert.ToInt64(DateTime.Now.Ticks) << 32) >> 32
));
Tree<int> tree = new Tree<int>();
for (int i = 1; i <= 0; ++i)
{
int x = random.Next(100);
Console.Write(x.ToString() + " ");
tree.Add(x);
}
Console.WriteLine();
foreach (int i in tree.WalkAround())
Console.Write(i.ToString() + " ");
Console.ReadLine();
}
}
class Tree<T> where T : IComparable<T>
{
private TreeNode<T> root;
public Tree()
{
this.root = null;
}
public void Add(T item)
{
if (root == null)
this.root = new TreeNode<T>(item);
else
this.root.AddChild(item);
}
public IEnumerable<T> WalkAround()
{
if (this.root != null)
foreach (T i in this.root.WalkAround())
yield return i;
else
yield break;
}
}
class TreeNode<T> where T : IComparable<T>
{
private TreeNode<T> leftChild;
private TreeNode<T> rightChild;
private T item;
public TreeNode(T item)
{
this.leftChild = null;
this.rightChild = null;
this.item = item;
}
public void AddChild(T item)
{
if (item.CompareTo(this.item) < 0)
if (this.leftChild == null)
this.leftChild = new TreeNode<T>(item);
else
this.leftChild.AddChild(item);
else
if (this.rightChild == null)
this.rightChild = new TreeNode<T>(item);
else
this.rightChild.AddChild(item);
}
public IEnumerable<T> WalkAround()
{
if (this.leftChild != null)
foreach (T i in this.leftChild.WalkAround())
yield return i;
yield return this.item;
if (this.rightChild != null)
foreach (T i in this.rightChild.WalkAround())
yield return i;
}
}
}
Операторы yield return и yield break работают таким образом, что прерывают исполнение методов на каждой итерации, т.е. фактически в программе одновременно выполняется множество циклов с периодическими переходами между ними при помощи замаскированных goto. Оно может и удобно для рекурсивных алгоритмов, вроде описанного выше, но с точки зрения понятности программы... мне кажется, такая конструкция вредна. По крайней мере создатели языка явно не сторонники Дейкстры и не читали "Дисциплину программирования". Каким образом доказывать корректность таких программ, я пока не знаю.
Остальные синтаксические нововедения мелкие и внимания, по-моему, не заслуживают. Например, "static class", предписывающий в классе объявлять только статические члены, запрещающий наследование такого класса и запрещающий создание объектов такого класса. Может ещё чего есть.