. Net очима дельфийцев – перші враження.

Частина 1. C #

Зміст

Введення

Чого немає в C #

Чого немає в Delphi

Висновок

Введення

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

Враховуючи те, що C #, як і Delphi, виступає
одночасно в двох якостях, тобто з одного боку, є
семантично суворо певною мовою програмування і, з іншого
боку, використовує поставляються в складі. Net бібліотеки класів і
компонентів, на першому етапі має сенс сконцентруватися на самому
мовою програмування, тому що вивчення та порівняльний аналіз бібліотек
класів – набагато більш об'ємна робота.

Чого немає в C #

Відсутність в C # деяких речей обумовлено
тим, що C # є <чисто> об'єктним мовою програмування, а Delphi
– Гібридним. Тим не менш, в C # або є, або можуть бути легко
реалізовані самостійно практично всі семантично еквівалентні
конструкції.

Отже, C # не надає наступні
можливості (їх розгляд не увійшло до цього документа в силу чи
другорядного значення, або наявності семантично еквівалентних
реалізацій в бібліотеці CLR):

Більш суттєві конструкції, яких немає в
C#:

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

Якщо вважати, що процедури – це просто
функції, які не повертають жодного значення, то семантична
навантаження процедур і функцій в 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). Однак, необхідно
відзначити, що в практичному програмуванні рідко доводиться
використовувати покажчики для інших цілей, окрім оптимізації
продуктивності. Тому, враховуючи класичне правило <80/20> (<80%
пива випивають двадцятьма відсотками населення>, або <80% використовуваних
ресурсів програми доводиться на 20% коду>, або <80% обсягу роботи
дозволяє поліпшити продуктивність тільки на 20%>), можна
акцентуватися на оптимізації коду (у термінах C # – використання
небезпечного коду) тільки при необхідності і тільки тоді, коли
виявлені ті самі 20% коду, які використовують 80% ресурсів.

Чого немає в Delphi

Тепер можна розглянути ті переваги,
які має C # в порівнянні з Delphi (порядок перерахування довільний
і ні в якій мірі не відображає об'єктивні пріоритети або суб'єктивні
переваги):

Опис змінних в коді програми

Можливість опису змінних в коді
програми прийшла в 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 реалізовано не кращим чином,
так як:

Більш того, <стандартні> об'єкти 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 для того, щоб реалізувати
концепцію <поле тільки для читання>, можна використовувати властивості
(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 # з Visual Basic.
Вийшла досить зручна річ:

	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 писати. Але робити це можна вже СЬОГОДНІ.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*