В 3.5.1.2277 программа выдаёт исключение System.NullReferenceException
:
На последней (2326) всё ещё воспроизводится.
Порой гнаться за обновлениями вредно… Выше пример. На мой взгляд, хорошо бы иметь две версии PascalABC.NET - одна обкатанная, другая - самая свежая, но для тестирования.
Это не обоснование. Вы даже не проверили воспроизводится ли у вас на 2326, может это из за .Net или чего то ещё.
Мне достаточно того, что есть человек, у которого данная ошибка воспроизводится. Выяснять почему и что не работает - задача разработчиков, а помогать им в этом - тестировщиков. Я не являюсь ни одним из них. А обновляться просто так, чтобы проверить есть ли ошибка или нет, когда в моей версии поведение адекватное - мне нет желания. Меня и 3.5.1.2277 более чем устраивает.
Можно увидеть пример инстанцирования для:
type
A<T> = class where T: A<T>;
end;
? В C#
- аналогичный код компилируется, но инстанцировать тип напрямую невозможно, как и здесь, впрочем, что правильно.
Кроме того, Вы утверждайте, что эта конструкция полезна, но я не вижу инстанцирования типа в Вашем же примере:
type
A<T> = class where T: A<T>;
end;
B = class(A<B>)
end;
begin
var x := new B;
Writeln(x is A);
end.
Именно в паскале не работает из за другой issue, но код правильный и в C# так сработает.
Как я и сказал - весь смысл такого паттерна в том, чтоб избежать дубли кода в наследниках. То есть весь этот такой класс нужен только для наследников.
Я не добавил abstract
в минимальный код, потому что это минимальный код. Но в примере (из #2190) он есть.
Ну и самое сладкое, зачем я это вообще придумал:
type
A<T> = class where T: A<T>;
static property p1[s: string]: T read default(T); default;
end;
B = class(A<B>)
end;
begin
var x := B['abc']; // у "x" тип будет B
end.
(ссылка на более реальный пример уже 2 раза выложена)
using System;
public class A<T> where T : A<T>
{
}
public class B : A<B>
{
}
public class Program
{
public static void Main()
{
var x = new B();
Console.WriteLine(x is A<B>);
}
}
У Вас пропущен тип-параметр для A<>
, так что нет, он неправильный.
type
A<T> = class
public
static property P[s: string]: T read default(T); default;
end;
B = class(A<B>)
end;
begin
var x := B['abc'];
end.
У x
также тип - B
, так что я не вижу смысла усложнять код, если можно без where
.
Вы можете словами объяснить, когда такое реально будет нужно? Не на абстрактном примере, не на каком-то там “фиксере” (который непонятно чем занимается), а на реальном примере из реальной жизни?
Я более чем уверен, что, если данный паттерн полезен, каким Вы его представляйте, то использовался и используется сейчас в проектах. Если найдёте как он называется - можете сюда дать ссылку, пусть люди почитают.
Ну а это не только не_безопасно. В примере который я уже привёл - используются поля типа Fixer
на этом же T
.
Как я и сказал, то как красиво наследуется статичное дефолтное свойство - это то что особо приятно. Но пример приведён полный.
Это не абстрактный пример, это то что мне понадобилось в своём коде. И я с радостью дам вам стенку текста, объясняющую всё. Очень хороший способ лишний раз самому всё перепродумать:
1. Группы в .xml
Для начала посмотрите сюда. В этом файле (и ещё 2 других .xml-ках рядом) хронос описывает весь интерфейс всех версий и расширений OpenGL.
Там так же есть так-называемые группы. Это аналог enum-ов из высокоуровневых языков.
Но программный интерфейс для OpenGL который предоставляет хронос - это .h файлы, то есть для языков C/C++. А там группировать константы не принято, вместо это в каждый GLenum
параметр можно передать любую константу.
Группы в .xml файлах сделаны только для тех, кто занимается враперами в высокоуровневых языках. И поэтому хронос сам не сильно ими занимается, так что у них есть некоторые проблемы.
issue, конечно, уже сделал, но делать исправления в своём форке не хочу, потому что тогда обновление под-репозитория в POCGL не будет так же просто как вызов git.exe
с одной командой.
Решение которое я выбрал - проверенное мною и временем множество раз - сделать отдельный файл, в котором будет что то типа:
#GroupName1
!rename=OtherName
#GroupName2
!addenum
ename=0x12AB
Ну и разумеется этому файлу нужен парсер и экземпляры какого то класса, который может хранить информацию об изменении в самом простом, в том числе в применении, виде.
Формат этот, кстати, я очень давно придумал, ещё во времена SAC (в итоге даже в синтаксис самого SAC попало). Весь смысл как раз в том, что его парсить предельно легко и быстро, нужны только чтение по 1 строке + базовые операции над string
. В то же время писать это - полегче чем даже .xml (хоть с безопасностью всё не так же хорошо).
2. Функции
Есть C++ функция:
void p1(int * x)
Что такое x
? var
-параметр? массив? Любая другая область памяти, из которой можно создать что то типа буфера в OpenGL/OpenCL (то есть тупо указатель)?
Это автоматически не решить. Надо продумывать для каждой функции отдельно. И хардкодить такое будет больно, поэтому, опять же, тот же формат.
# p1
T%2
-array of integer |
(второй уровень изменений перегрузки, из возможных типов первого параметра убираем массив, оставляя указатель и var
-параметр. “|” вообще разделяет параметры, но тут он только для виду)
Это уже реализовано. Но я всё равно хочу немного переделать, сделать чтоб всё смотрелось более одинаково.
3. Описания
(и другие строки которые лучше вынести, как тексты исключений)
Пока используется только в OpenCLABC
, но так же планируется для OpenGLABC
и может ещё чего-нибудь.
# __NativUtils
# __EventList
%hidden%
В другом файле:
# hidden
--
Применение, конечно, немного другое, но в итоге функционал нужен всё тот же.
4. Другое
Возвращаясь к п.1. - группы это не единственное что может быть не правильно. Я ещё не дошёл до проверки этой части .xml, но в .txt файлах описывающих расширения было много неравномерностей и в целом кривоты.
И кто знает, для чего ещё может понадобится фиксер. И не только в районе групп, мне ещё предстоит для OpenCL подобное делать, а там таких же удобных .xml-ок нету, то есть придётся, наверное, снова парсить .pdf и .txt (как я делал с OpenGL, пока не понял что есть .xml) и потом исправлять за парсером, опять же, фиксерами.
Прямо сейчас пункты 2 и 3 используют отдельный код, который, по сути, одинаковый с минимальными изменениями.
И вот делая это же третий раз (для групп), ещё и понимая что наверняка придётся ещё раз потом - я решил сделать промежуточный класс, в котором будет:
Ядро парсера
Это одна из вырезанных из примера вещей:
protected static function ReadBlocks(lines: sequence of string; power_sign: string): sequence of (string, array of string);
begin
var res := new List<string>;
var name: string := nil;
foreach var l in lines do
if l.StartsWith(power_sign) then
begin
if res.Count<>0 then
begin
yield (name, res.ToArray);
res.Clear;
end;
name := l.Substring(power_sign.Length).Trim;
end else
res += l;
if res.Count<>0 then yield (name, res.ToArray);
end;
protected static function ReadBlocks(fname: string) := ReadBlocks(ReadLines(fname), '#');
, словарь All
и вся его свита… Ну и всякие дополнительные вещи, как WarnUnused
/WarnAllUnused
, которые тоже нужны в одинаковом виде для всех фиксеров.
А теперь внимание вопрос: Хоте ли вы действительно всё это знать?)))
То что у типа в примере имя не t1
, t2
, t3
/ c1
, c2
, c3
, а что то что явно что то значит, Fixer
- уже говорит что это что то из реального кода, который мне реально понадобился.
Хотя было бы приятно, если бы вам действительно хотелось разобраться в том как я неправильно что то использую. Однако, честно, не верю и не поверю. Но в данном случае мне на-руку такая трата моего времени (почему - уже сказал).
И, хотите верьте, хотите нет - именно этого вопроса я ожидал и надеялся когда писал предыдущие 2 сообщения. Доказывать это я, разумеется, не собираюсь (даже если бы было возможно).
Покажите мне где это небезопасно, конкретно в примере с индексным свойством по умолчанию.
И в чём же там красивость? В лишнем where
? Конкретно в данном примере where
ни к чему. Вы его даже не используйте. Не надо мне в который раз рассказывать про какие то “фиксеры” - кратко, чётко и общо - вот что надо. Вы слишком углубляйтесь в детали реализации своих модулей и думаете, что другим:
- они будут интересны (хотя, почему нет?)
- будут понятны (всем ли?)
Не все так увлечены OpenGL
/OpenCL
, чтобы понимать Ваши примеры; если хотите, чтобы Вас понимали приводите более простые примеры, для “простых смертных”.
Мне бы хватило краткого объяснения зачем это нужно. Точнее:
- Описание паттерна
- Реализация (общий код, не привязываясь к конкретной задаче)
- Когда нужен и когда без него нельзя обойтись
Ни первого, ни второго, ни третьего я так и не получил в чёткой форме, вместо - Вы на меня вылили гору текста. Посмотрели бы как описываются паттерны на refactoring.guru. Мне не нужно знать как и что Вы там реализуете, мне это неинтересно, мне главное было понять как, когда и зачем Ваш паттерн, который Вы называйте полезным, применять.
Я не называл конкретно Ваш пример абстрактный, я лишь сказал, что нужно объяснить на реальном примере зачем это всё нужно, не на каком-то куске кода без комментариев и не горой текста, которая выше.
В том что T
это по задаче всегда фиксер. Иначе придётся as object as Fixer
писать, что и есть небезопасно.
Ещё раз, это то что приятно о использовании общего базового типа. То, что можно написать одно индексное свойство и для каждого типа оно будет возвращать его же. where
вообще не при чём.
Вы не поверите, но я не могу дать вам упрощённый вариант кода для того, что я ещё не написал.
Но основной смысл проще-некуда, этот код уже был выложен, только в ещё более упрощённой форме:
type
Fixer<T> = abstract class where T: Fixer<T>;
// всё остальное это общий интерфейс всех фиксеров,
// второй раз его прописывать не вижу смысла
protected procedure WarnUnused; abstract;
end;
GroupFixer = abstract class(Fixer<GroupFixer>)
static constructor; // занимается заполнение словаря "All"
// Group это, конечно, класс
public procedure Apply(gr: Group); abstract;
protected procedure WarnUnused; override :=
// PackAll.pas подсвечивает в консоли слова как WARNING,
// а потом это всё сохраняет в .log файл, так что достаточно
Writeln($'WARNING: Fixer of group [{self.name}] wasn''t used');
end;
begin end.
Ну а от GroupFixer
уже наследуют все фиксеры для лично групп, каждый наследник хранит и выполняет какой то свой тип исправления.
Вот только в итоге это ничего сильно то не объясняет. Кроме того, вы сами попросили именно
Чужие проекты я искать не собираюсь, пустая трата времени. Позиция “всё что можно - сделано, а что не сделано - неправильно” сама фундаментально неправильная и спорить об этом я не буду.
Если что кратко по пунктам:
- Только что точнее некуда описал. Хотя в целом описание было давно. Документацией их не назовёшь, но это не то что я пытался дать.
- Её у меня нет, как и сказал, потому что ещё не написал. Есть только продублированный код, от которого я как раз избавляюсь этим паттерном. Или пытаюсь.
- Несколько раз сказано, надо для избежания дублей кода.
А в общем случае что это должно быть? Посмотрите refactoring.guru и охарактеризуйте Ваш “фиксер” примерно теми же терминами, что используют там в UML-схемах, иначе непонятно.
Можно было бы и написать, если хотели бы, чтобы Ваш паттерн поняли.
Уже в который раз слышу подобную фразу. Признайтесь - лень. И каждый раз Вы пытаетесь выкрутиться подобными шаблонными фразами, не надоело ли?
Можно было бы и лучше. Что такое этот Ваш “фиксер” - мне до лампочки. От того, что Вы будете часто говорить это слово я понимать лучше не стану. Посмотрите как сделаны UML
-схемы - чётко, кратко и понятно и описание к ним такое же. Сделали бы также, плюс код и всё было бы хорошо.
Паттерны многие для данной цели существуют. Это не ответ.
Общий случай паттерна придуманного для фиксера ограничивается фиксером. Не надо пытаться абстрагироваться ниже пола.
Ещё 2 абзаца (не в том же порядке) бесполезны по этой же причине.
Это мне не нужно.
В issue я кинул обрезанный кусок своего кода, только чтоб напомнить что вообще где то там такой паттерн у меня реально использовался. По моему опыту, если написать больше - разработчики могут проигнорировать. Ну а подробнее написать всегда можно, если вдруг окажется мало.
А что до вас - я потратил ваше время при очередной попытке вами потратить моё время, по не стОящей того теме. И получил от этого пользу. К примеру, получил прекрасный приятный повод остаточно перечислить где этот паттерн мне понадобится. Ну и + так в целом по мелочам
(с новой стороны*)
Очень жаль что вы всё ещё не пытаетесь отойти и посмотреть на всю ситуацию со стороны, не смотря на все намёки (если их даже можно так назвать, при их прямоте).
Абстракция это обобщение, так что про пол - это неверная аналогия.
Тогда незачем кидать какой-то кусок кода, даже не удосужившись по-нормальному объяснить что да как, в общем случае, если это паттерн.
Неудивительно если проигнорируют, потому что Вы очень “постарались” им доходчиво объяснить нужный Вам паттерн. Не умеете нормально объяснять - пеняёте только на себя. Здесь Вас не будут учить объяснять, не умеете - учитесь, не хотите - извините. Вы не заставите разработчиков копаться в Ваших “фиксерах”, но Вы можете доходчиво им объяснить в общем случае и кратко проблему, чтобы они поняли. Остаётся одно - Ваше желание.
Вы переходите на более общий и низкий уровень, чем надо. Ниже пола.
Раз уж надо всё так разжёвывать - низкий уровень не в том же смысле что низкоуровневое программирование.
Надо кому? Вам? Потому что Вам не хочется обобщать? Тогда уж извините, то если разработчики проигнорируют Ваш комментарий, то я буду с ними полностью солидарен, хотя считаю, что это несколько жёстоко по отношению к пользователю запрещать что-то просто потому что он не потрудился и не изложил хорошо свою мысль. Я бы не закрывал подобные вещи, а требовал бы от автора-тестировщика по-нормальному изложить свою мысль и ждал бы сколько нужно. Здесь не телепаты, не будут за Вас догадываться что-же Вы хотите сказать. Тем более, Вы сами вызвались тестировать данный проект и уже являетесь фактически его тестировщиком и Вам как никому другому надо уметь правильно выражать свои мысли и обобщать.
Это только в NET Core
Ещё раз о том, что не надо пытаться ожидать, что в PascalABC.NET будет как C#
. Это разные языки, хоть и на одной платформе… Кто хочет C#
с begin
-end
- пусть идут на C#
, тут - Паскаль.
В ядре ничего не правилось. Это проблема локального компьютера. Issue закрыта.