Преимущества синтаксиса PascalABC.NET перед C#

В этой теме будут выкладываться и обсуждаться синтаксические преимущества PascalABC.NET перед C# !

Статические индексные свойства по умолчанию

Уникальными по сравнению с C# кажутся статические индексные свойства (по умолчанию). В C# нет даже просто статических индексаторов, отчего простейший для PascalABC.NET код:

type
  T = class
    private static property _prop[ind: integer]: integer read Arr(1, 2, 3)[ind];
  end;

Для C# оказывается невозможным.

Но фича не была бы фичей, если бы от неё не было толку! Поговорим о применении статических индексных свойств по умолчанию. Эта конструкция очень полезна, когда есть объекты типа T и к ним нужен удобный доступ. Например, если в вашей программе есть база данных городов, можно написать следующий код:

type
  Town = class
	private fPop: uint64;
	
	public constructor(Pop: uint64) := fPop := Pop;
	
	public property Population: uint64 read fPop;
	
	private static ftowns: Dictionary<string, Town>;
	
	private static property _towns[ind: string]: Town read ftowns[ind] write ftowns[ind] := value; default;
	
	public static procedure Init;
	begin
	  //Заполнение может быть из файла, с клавиатуры, да вообще как угодно
	  ftowns := new Dictionary<string, Town>;
	  ftowns.Add('Челябинск', new Town(3490000));
	  ftowns.Add('Ростов', new Town(1100000));
	  ftowns.Add('Владивосток', new Town(600000));
	end;
	
	private static constructor := Init;
  end;

И мы сможем обратиться к городу просто Town[‘townname’]!

begin
  Writeln(Town['Челябинск'].Population);
end.

Теперь о применении на практике. В одной из реализаций одного своего проекта весь текст, который я использовал в интерфейсе программы, хранился в одном классе. Вот так выглядел тот класс:

type
  Text = static class
	private static filename := 'bin\textdb.dat';
	
	private static fText: array of string;
	
	private static fTextCount: integer;
	
	private static property ffText[ind: integer]: string read fText[ind] write fText[ind] := value; default;
	
	public static procedure ReloadText;
	begin
		//Здесь происходило заполнение
	end;
  end;

И пример обращения:

  Console.SetCursorPosition(52, 5 + id); write($'{Text[5200]}: {Savegame.Saves[Stage].Player.Nickname}');
  Console.SetCursorPosition(52, 6 + id); write($'{Text[5201]}: {Savegame.Saves[Stage].Player.Level}');
  Console.SetCursorPosition(52, 7 + id); write($'{Text[5202]}: {Savegame.Saves[Stage].Player.Experience} / {Player.fNeedExp(Savegame.Saves[Stage].Player.Level)}');
  Console.SetCursorPosition(52, 8 + id); write($'{Text[5203]}: {Savegame.Saves[Stage].Player.FreePerks}');
  Console.SetCursorPosition(52, 9 + id); write($'{Text[5204]}: {Savegame.Saves[Stage].Player.Money / 100} {Text[299]}');
  Console.SetCursorPosition(52, 10 + id); write($'{Text[5205]}: {Map[Savegame.Saves[Stage].Player.Position].Type.Name} {Map[Savegame.Saves[Stage].Player.Position].Name}');

Как эта часть Text выглядела в базе данных:

00005200	Ник
00005201	Уровень
00005202	Опыт
00005203	Нераспределённые очки
00005204	Деньги
00005205	Местоположение

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