• Microsoft .NET
  • C#.NET
  • Делегаты каркаса .NET: Основные сведения об асинхронных делегатах

.NET глазами дельфийца. C#

ОГЛАВЛЕНИЕ

При знакомстве с новым языком программирования любогопрограммиста в первую очередь интересует семантическая основа языка, т.е.насколько его выразительные возможности позволяют реализовать привычныелогические конструкции.Учитывая то, что C#, как и Delphi, выступает одновременнов двух качествах, т.е. с одной стороны, является семантически строгоопределенным языком программирования и, с другой стороны, используетпоставляемые в составе .Net библиотеки классов и компонентов, на первом этапеимеет смысл сконцентрироваться на самом языке программирования, т.к. изучение исравнительный анализ библиотек классов - гораздо более объемная работа.


Чего нет в C#

Отсутствие в C# некоторых вещей обусловлено тем, что C# является объектным языком программирования, а Delphi - гибридным. Тем не менее, в C# или имеются, или могутбыть легко реализованы самостоятельно практически все семантическиэквивалентные конструкции.

Итак, C# не предоставляет следующие возможности (их рассмотрение не вошло внастоящий документ в силу или второстепенного значения, или наличиясемантически эквивалентных реализаций в библиотеке CLR):

  • значения параметров по умолчанию
  • множества (set) - реализуется в виде специальных классов в библиотеке CLR
  • диапазоны (subrange) - реализуется в виде специальных классов в библиотеке CLR
  • синонимы простых типов
  • ресурсные строки (resourcestring) - рассматривается как частный случай констант

Более существенные конструкции, которых нет в C#:

  • процедуры, функции
  • глобальные константы
  • глобальные переменные
  • предварительное объявление типов
  • типизованные константы
  • const-параметры
  • указатели


Процедуры, функции

Если считать, что процедуры - это просто функции, которыене возвращают никакого значения, то семантическая нагрузка процедур и функций вDelphi одинакова. Это- выполнение некоторого фрагмента кода, который, возможно, зависит от входныхпараметров:


procedure A(aParam: integer);
begin
// ...
end;
function B(aParam: integer): integer;
begin
// ...
Result := 0;
end;
A(1);
X := B(1);

В C# семантическим эквивалентом процедур и функций выступают статическиеметоды классов.


// класс-обертка
class Func {
// статический метод без возвращаемого значения - эквивалент процедуры
static public void A(int aParam);
// статический метод - эквивалент функции
static public int B(int aParam);
}
// вызов процедуры
Func.A(1);
// вызов функции
int X := Func.B(1);

 

Глобальные константы

Семантическая нагрузка в Delphi - определение значений примитивныхтипов данных, доступных из любого места кода и неизменяемых в процессевыполнения программы.


const A = 100;
const B = 'строка';
D := A;
ShowMessage(B);

Семантический эквивалент в C# - статические константы.


// класс-обертка
class Const {
// описание констант
public const int A = 100;
public const string B = "строка";
}
// использование констант
int a = Const.A;
MessageBox.Show(Const.B);

Кроме статических констант C# предоставляет механизм статических полей, который позволяет программисту использовать вкачестве констант не только примитивные значения, но и объекты. Примеркода:


// класс-обертка
class Const {
// число-константа
public static readonly int A = 1;
// объект-константа
public static readonly MyObject Obj = new MyObject();

Глобальные переменные

Семантическая нагрузка в Delphi - формирование объектов программы (какпримитивных типов, так и сложных), доступных из любого места кода и, возможно,изменяемых в процессе выполнения программы.


var A: integer;
B := A;
A := 1;

Семантический эквивалент в C# - статические поля классов.


// класс-обертка
class Globals {
// определение статических переменных
// инициализация по умолчанию = 0
public static int A;
// одновременные описание и инициализация
public static int B = 1;
}
// использование статических переменных
int a = Globals.A;
Globals.A = 1;
int b = Globals.B;
Globals.B = 1;

 

Предварительное объявление типов

Предварительное объявление типов на самом деле непредусмотрено общей теорией объектно-ориентированного программирования иявляется частным решением Delphi, направленным на ослабление правила, которое было введено еще вклассическом Pascal,- .

Пример кода на Delphi:


type
TMyObject1 = class;
TMyObject2 = class;
TMyObject1 = class
function GetChild(Index: int): TMyObject2;
end;
TMyObject2 = class
property Owner: TMyObject1 read fOwner;
end;

В C# предописание типов не требуется, т.к. в пределах области видимостиклассов (обрамляющий класс, пространство имен) порядок объявлениянесущественен. Такое решение упрощает написание кода:


namespace MyObjects {
public class TMyObject1 {
public TMyObject2 GetChild(int Index) { ... }
}
public class TMyObject2 {
public TMyObject1 Owner {
get { return owner; }
}
}
}

 

Типизированные константы

Типизированные константы в Delphi позволяют хранить не только значенияпримитивных типов, но и массивы, записи, а также указатели, включая указателина процедуры и функции:

	const A: integer = 1;
const B: array [1..3] of integer = (1, 2, 3);

В C# константы всегда типизированы - как при использовании модификатораconst, так и readonly.

В Delphi при использовании директивы компилятора {$J+} (установлено поумолчанию) типизованные константы ведут себя как обычные переменные, которыеинициализируются одновременно с описанием, т.е. их значение может быть измененов ходе выполнения программы:

	const A: integer = 1;
X := A;
A := 2;

В C# действуют более строгие правила - если константа, то поменять еезначение невозможно. Если же используется поле , тоего содержимое может быть изменено в контексте объекта:

	// класс-обертка
public class Const {
// число-константа
public readonly int A = 1;
// метод изменяет значение поля A
public void ChangeA(int NewValue) { A = NewValue; }
}

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

	procedure A;
const B: integer = 0;
begin
Inc(B);
ShowMessage(IntToStr(B));
end;
procedure Do;
begin
A;
A;
A;
end;

Результаты вывода: 1, 2, 3

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

Const-параметры

В Delphi семантический смысл const-параметров заключается в указаниикомпилятору на возможность оптимизации передачи в функцию (процедуру, метод)неизменяемой ссылки на некоторый объект программы. Так, например, конструкциятипа:


procedure A(const S: string);

означает, что в качестве параметра процедуры будетпередаваться ссылка на строку (при этом копирования содержимого строки в стеквызова процедуры не происходит). Кроме того, содержимое строки S внутри процедуры изменитьнельзя.

В C# не предусмотрено прямого эквивалента const-параметров. Тем не менее, в случаенеобходимости может быть построена семантически эквивалентная конструкция(аналогия вышеприведенному примеру):


class ReadOnlyString {
ReadOnlyString(string S) { this.S = S; }
public readonly string S;
static void Test(ReadOnlyString s) { Console.Write(s.S); }
static void Main() {
string s = "проверка const-параметров";
ReadOnlyString.Test(new ReadOnlyString(s));
}
}

Приведенный код иллюстрирует использованиеклассов- (т.н. wrappers) и полей.


 

Указатели

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

В C# приведенный пример использования указателей на записи более изящно ибезопасно реализуется с использованием такой конструкции, как struct. Структуры, как и записиDelphi, могутиспользоваться для хранения данных и, что более важно с точки зрения семантики,являются объектами, передаваемыми не по ссылке, а по значению.

На самом деле, в C# имеется всего лишь одна возможностьиспользовать указатели - т.н. код (unsafe code). Особенности его использованияопределены в стандарте C# достаточно подробно (спецификация C#, приложение A). Однако, необходимо отметить, что впрактическом программировании редко приходится использовать указатели для иныхцелей, кроме оптимизации производительности. Поэтому, учитывая классическоеправило (, или , или ), можно акцентироваться на оптимизации кода (в терминах C# - использованиенебезопасного кода) только при необходимости и только тогда, когда выявлены тесамые 20% кода, которые используют 80% ресурсов.


 

Чего нет в Delphi

Теперь можно рассмотреть те преимущества, которые имеетC# по сравнению сDelphi (порядокперечисления произволен и ни в коей мере не отражает объективные приоритеты илисубъективные предпочтения):

  • описание переменных в коде программы
  • возможность передачи в метод переменного количества параметров
  • автоматическое удаление объектов
  • поля классов
  • индексаторы
  • делегаты
  • возможность реализации модели обработки событий
  • цикл foreach
  • статические конструкторы
  • операторы классов
  • структуры
  • атрибуты
  • возможность использовать русский язык для имен объектов программы

Описание переменных в коде программы

Возможность описания переменных в коде программы пришла вC# из C++. Пример кода:


class VarTest {
static void Main() {
int a;
int b, c = 1, d;
for (int i = 0; i < 10; i++) {
int x = i * 2;
}
}
}

В этом примере продемонстрированы сразу нескольковозможностей, отсутствующих в Delphi:

  • описание переменных
  • одновременное описание и инициализация переменных
  • локальная область видимости и время жизни переменных

Возможность передачи в метод переменного количества параметров

Пример кода, иллюстрирующий возможность передачи в методпеременного количества параметров:


class Test {
static void F(params int[] args) {
// обработка параметров
}
static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(new int[] {1, 2, 3, 4});
}
}

В Delphi отсутствие переменного количества параметров можно частичноскомпенсировать либо использованием значений параметров по умолчанию, либопередачей в качестве параметра открытого массива.

Однако в первом случае все равно количество параметровограничено и должно быть определено при написании соответствующейподпрограммы.

Во втором же случае, хотя и наблюдается сходство с C#, есть существенныеограничения:

  • нельзя передать пустой массив (т.е. массив с нулевым количеством элементов)
  • передаваемый массив должен быть создан и заполнен или до вызова подпрограммы, или в момент вызова с помощью конструктора открытого массива
  • неоднородные типы данных поддерживаются только при передаче вариантного открытого массива (впрочем, код разбора такого массива внутри подпрограммы обычно бывает довольно
  • громоздким).


Автоматическое удаление объектов

При программировании в терминах объектов Delphi приходится постоянноучитывать, когда создаются объекты определенного класса и, что еще важнее, ктои когда их удаляет.

Ситуация несколько упрощается при построении визуальныхприложений и использовании компонентов - установив компонент на форму (илимодуль данных), тем самым мы определяем механизм создания и удаления объектовсоответствующего класса, когда форма-владелец создает объект в процессе своейинициализации и удаляет его в момент своего удаления.

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

Впрочем, необходимо признать, что автоматическое удалениеобъектов в Delphiреализовано не лучшим образом, т.к.:

  • приходится использовать две или даже три разные модели программирования для визуальных и невизуальных приложений (управление компонентами посредством формы-владельца, интерфейсные объекты, COM-объекты);
  • при использовании COM-объектов отладка не является прозрачной с точки зрения объектной модели Delphi, т.к. в целях совместимости со стандартами OLE приходится соблюдать ряд ограничений, в т.ч. в вопросах генерации ошибок, совместимых с COM.

Более того, объекты Delphi, которые часто используются припостроении прикладных объектов, например, списки, стеки, битовые массивы и пр.,не поддерживают механизм автоматического удаления, поэтому на практикеприходится или использовать смешанную модель, в которой лишь некоторые объектыудаляются автоматически, или в целях стандартизации кода вообще отказываться отавтоматического удаления.

Рассмотрим простую семантическую конструкцию - загрузкусписка объектов.

В Delphi типичная реализация выглядят примерно так:


procedure LoadList(aList: TObjectList);
begin
aList.Clear;
// заполнение списка
end;
. . .
try
MyObjectList = TObjectList.Create;
LoadList(MyObjectList);
// далее - использование объектов из списка
finally
MyObjectList.Free;
end;

На самом деле из-за ограничений Delphi (TObjectList не может удаляться автоматически)семантика приведенного кода разбивается на две отдельные фазы:

  • создать пустой список объектов
  • заполнить список

В C# аналогичное действие (загрузка списка объектов) реализуется проще исемантически точнее:


class ListLoadTest {
Collection LoadList() {
Collection c = new Collection();
// непосредственная загрузка объектов и добавление их в коллекцию
return c;
}
static void Main() {
Collection c = LoadList();
foreach (MyObject in c) {
// что-то сделать с очередным объектом из списка
}
}
}

Строго говоря, в приведенном коде C# даже два преимущества:

  • во-первых, не нужно думать об удалении списка объектов и его элементов (если бы в Delphi для хранения объектов вместо TObjectList использовался бы TList или просто массив, объекты пришлось бы удалять вручную)
  • во-вторых, список объектов создается и заполняется в одном месте.

Таким образом, довольно простая вещь - автоматическоеудаление объектов, - позволяет писать более точный (семантически) и понятныйкод.


 

Поля классов

В Delphi для того, чтобы реализовать концепцию , можно использовать свойства (properties), при этом приходится писать нечтоподобное:


type
TMyObject = class
private
fData: integer
public
// эквивалент поля для чтения
property Data: integer read fData;
end;

Поля в C# введены на уровне языка:


class A {
public readonly int Data;
}

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

Это приводит к интересным последствиям. В стандарте C# рассматриваетсяситуация, когда имеется библиотека и использующая ее программа, компилируемыераздельно. Если в библиотеке использовать константу, то при изменении еезначения (и перекомпиляции библиотеки) нужно перекомпилировать и программу.Если же использовать поле , то программуперекомпилировать не обязательно, т.к. значение поля определяется на стадииисполнения.


 

Индексаторы

В Delphi можно реализовать свойство класса типа массив и, установив для него атрибут default,получить некоторое подобие индексатора:


type
TMyObject = class
public
property Items[Index: integer]: string read GetItem; default;
end;

Тогда в коде можно использовать две эквивалентныеконструкции:


S := MyObject.Items[I];
S := MyObject[I];

Вторая строка как раз и демонстрирует основную идеюиндексаторов C# -возможность обращаться к объекту как к массиву. Однако в Delphi есть существенное ограничение -можно использовать только одно свойство (типа массива) по умолчанию.

В C# можно реализовать произвольное количество индексаторов длякласса:


class A {
int this[int Index] { . . . }
string this[char Col, int Row] { . . . }
static void Main() {
A a = new A();
for (int i = 0; i < a.Count; i++)
Console.Writeln(a[i].ToString());
for (char c = 'a'; c < 'z'; c++)
for (int r = 1; r < 100; r++)
Console.Writeln(a[c, r]);
}
}

 

Делегаты

В Delphi обработчики событий играют роль делегатов - реальную работу внешнему объекту. Однако из-за того, что Delphi является гибридным языкомпрограммирования, встречаются ситуации, когда семантически эквивалентные задачиреализуются разными способами. Так, например, в класса TList при сортировке используетсяуказатель на функцию сравнения элементов:


type TListSortCompare = function (Item1, Item2: Pointer): Integer;
procedure Sort(Compare: TListSortCompare);

Т.е. на самом деле задача сравнения элементов списка также функции, внешней по отношению к объекту TList.

Такая семантическая неоднозначность отнюдь не упрощаетпрограммирование.

В C# реализован более общий и однозначный подход - делегаты:


delegate void SimpleDelegate();
class Test {
static void F() {
System.Console.WriteLine("Test.F");
}
static void Main() {
SimpleDelegate d = new SimpleDelegate(F);
d();
}
}

Причем в качестве реализации делегата может выступать какстатический, так и обычный метод.

Возможность реализации модели обработки событий

Хотя по умолчанию в C# компоненты реализуют схему подключенияобработчиков событий , при необходимости может бытьреализована модель . Основа такой возможности заключается втом, что в качестве обработчиков событий используются делегаты, а ихподключение к компоненту реализуется с помощью перегружаемого метода:

	class Control: Component {
// Unique keys for events
static readonly object mouseDownEventKey = new object();
static readonly object mouseUpEventKey = new object();
// Return event handler associated with key
protected delegate GetEventHandler(object key) {...}
// Add event handler associated with key
protected void AddEventHandler(object key, Delegate handler) {...}
// Remove event handler associated with key
protected void RemoveEventHandler(object key, Delegate handler) {...}
// MouseDown event
public event MouseEventHandler MouseDown {
add { AddEventHandler(mouseDownEventKey, value); }
remove { RemoveEventHandler(mouseDownEventKey, value); }
}
// MouseUp event
public event MouseEventHandler MouseUp {
add { AddEventHandler(mouseUpEventKey, value); }
remove { RemoveEventHandler(mouseUpEventKey, value); }
}
}

Приведенный код взят из спецификации C# и, хотя выглядит относительнообъемным, прозрачно иллюстрирует возможность использования произвольноговнутреннего механизма для хранения ссылок на обработчики и подключенияпроизвольного количества внешних обработчиков.


 

Цикл foreach

Цикл foreach перенят в C# из VisualBasic. Получилась довольно удобная вещь:


class Test {
static void Main() {
double[] values = {1.2, 2.3, 3.4, 4.5};
foreach (double elementValue in values)
Console.Write("{0} ", elementValue);
}
}

Семантически аналогичный код в Delphi выглядит более громоздким из-занеобходимости использовать итератор (переменная I), а также (в общем случае) вычислять границымассива:


procedure A;
const Values: array [1..4] of double = (1.2, 2.3, 3.4, 4.5);
var I: integer;
begin
for I := Low(Values) to High(Values) do
ShowMessage(FloatToStr(Values[I]));
end;

 

Статические конструкторы

Некоторый семантический аналог статическим конструкторам вDelphi - секция initialize.

К сожалению, вDelphi порядок вызова секций initialize соответствует порядку подключениямодулей. Такая практика может приводить к неожиданным ошибкам - первоначальнорассчитывая на конкретный порядок подключения модулей, можно случайно впроцессе разработки изменить этот порядок и в результате, например, получитьобращение к несуществующему или некорректно инициализированному глобальномуобъекту программы.

C# предоставляет более строгое объектное решение, которое, в частности,позволяет управлять правами доступа:


class A {
static protected A GlobalA;
static A() { GlobalA = new A; }
}

В C# порядок работыстатического конструктора определен только на уровне класса, при наличии женескольких классов со статическими конструкторами порядок их активизации нефиксирован. Такой подход заставляет более тщательно проектировать программу ссамого начала и исключает появление в последующем ошибок, аналогичных описаннойвыше.


 

Операторы классов

Операторы классовв C# почти эквивалентны операторам классов в C++:


public class Digit {
byte value;
public Digit(byte value) {
if (value < 0 || value > 9) throw new ArgumentException();
this.value = value;
}
public static Digit operator+(Digit a, Digit b) {
return new Digit(a.value + b.value);
}
static public Main() {
Digit a = new Digit(5);
Digit b = new Digit(3);
Digit plus = a + b;
}
}

По сравнению сC++ в C# строго и однозначно определен порядок реализации пользовательскихправил преобразования объектов (преобразования рассматриваются как частныйслучай операторов).

Примечание: Delphi не имеет механизмов,эквивалентных операторам классов.


 

Структуры

Структуры в C# аналогичны записям в Delphi в том смысле, что являютсяданными, передаваемыми по значению, а не по ссылке.

На самом деле семантика структур в C# ближе к классам, за исключением двухосновных ограничений:

  • структуры не могут быть абстрактными или содержать абстрактные методы;
  • наследование от структур не поддерживается, в связи с чем методы структур не могут быть виртуальными.

Пример структуры:


struct Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

Использование структур может как повыситьпроизводительность программы (например, при размещении большого количествамелких объектов лучше использовать структуры), так и ухудшить ее (еслииспользуется структура, содержащая большие объемы данных, то при передаче ее вкачестве параметра будет выполняться лишнее копирование).

Существует эмпирическое правило: если объем данных меньше16 байт, то для их хранения лучше использовать структуру, если больше -класс.


 

Атрибуты

Интереснейшая возможность C#, отсутствующая как в Delphi, так и в других наиболеепопулярных языках программирования (VB, C++, Java), - атрибуты:


[Help("http://www.microsoft.com/.../Class1.htm")]
public class Class1 {
[Help("http://www.microsoft.com/.../Class1.htm", Topic = "F")]
public void F() {}
}

Атрибуты похожи на свойства классов Delphi, за исключением того, что ихзначения устанавливаются на стадии компиляции и в процессе выполнения программымогут быть только считаны. Однако сфера применения атрибутов в поставляемойбиблиотеке классов CLR весьма широка - от хранения вспомогательной информациидекларативного характера до обеспечения совместимости объектов .Net с COM (атрибуты совместимости с COM описаны в приложении B спецификации C#).

Привычки, сформированные под влиянием Delphi, не позволяют даже сразупридумать, для чего можно использовать атрибуты, однако поле деятельности здесьпросматривается широкое - от отслеживания версий алгоритмов до контроля засовместимостью программ.


 

Возможность использовать русский язык для имен объектов программы

В соответствии со спецификацией C# для именования объектов программы (классы,методы, переменные и пр.) используются символы Unicode. Отсюда следует (и реально проверено),что в качестве имен можно использовать русские названия, например:

	class ЭлементУчета {
public int readonly ИнвентарныйНомер;
public string readonly Наименование;
public double РассчитатьАмортизацию(int ЛетВЭксплуатации) { ... }
}

Конечно, использование русского языка в коде программы - вопрос спорный. Тем не менее, такая возможность есть. Использовать же ее или нет - вопрос стандартов написания кода в пределах рабочей группы (отдела, предприятия, корпорации).


 

Заключение

Безусловно, изложенные выше моменты не могут претендовать на абсолютную полноту и глубокую детализацию. Тем не менее, даже на их основе напрашивается естественный вывод - C# является весьма интересным языком программирования, по сравнению с Delphi во многих аспектах даже более мощным и в то же время строгим.

Можно ли на нем писать реальные промышленные задачи?

Если ориентироваться на монолитные приложения в стиле АРМ, то скорее всего нужно более детально проанализировать возможности исполнения программ .Net на конкретной аппаратной платформе (навряд ли Framework.Net сможет работать на 486 с 8М ОЗУ). Если же сделать акцент на многоуровневых приложениях, то C# для реализации промежуточного слоя по сравнению с Delphi
является более перспективным кандидатом.

Общий ответ на поставленный вопрос - писать реальные задачи на C# вполне можно, и, скорее всего, даже с меньшими затратами, чем на Delphi.

Трудно ли дельфийцу освоить C#?

Можно утверждать, что нет, т.к. в основе и Delphi, и C# лежит парадигма объектно-ориентированного программирования. Программист, который может в Delphi писать код в терминах объектов, а не только на уровне обработчиков стандартных компонентов, сможет писать и на C#.

Впрочем, C# - не единственный язык, который можно использовать в .Net. Даже в штатной поставке
Visual Studio .Net вместе с C# идет C++ и VB.Net, а вообще список языков программирования,
реализованных для платформы .Net, уже на сегодня перевалил за десяток. Может быть, и Borland
когда-нибудь выпустит Delphi.Net - не зря ведь C# является как раз автор Delphi.

Ждать ли дельфийцам выхода Delphi.Net?

На этот вопрос может ответить только фирма Borland. На самом деле, если глубже поразбираться в вопросе декларируемой совместимости программ, написанных на Delphi 6 и Kylix, то оказывается, что
о полной совместимости речь не идет - даже у одноименных классов VCL и CLX встречаются разные интерфейсы. Поэтому можно предположить, что Delphi.Net будет совместима с Delphi тоже не на все 100%. В чем же тогда будет преимущество Delphi.Net перед тем же C#? Для дельфийца - только в знакомом синтаксисе. Ну а пока Delphi.Net выйдет в свет, можно и C# освоить (тем более, что его
корни лежат в C++, а C++ всегда считался более мощным языком, чем Pascal), или можно на VB.Net
писать. Но делать это можно уже СЕГОДНЯ.