Видимость типов перечисления и помощников

У меня есть несколько базовых единиц, которые мне нужно держать отдельно, но для простоты использования я хотел бы сгруппировать их в одну единицу. На данный момент у меня проблема с типами перечисления и помощниками, не уверен, что еще что-то. Можно ли сделать эту работу?

Базовый блок

type
  TCustomEnum = (ceValue1, ceValue2, ceValue3);

TCustomEnumHelper = record helper for TCustomEnum
  function AsString: string;
end;

Группа

uses BaseUnit;

TCustomEnum = BaseUnit.TCustomEnum; 

Использование в других единицах.

uses GroupUnit;

procedure DoSomething;
var
  lValue : TCustomEnum;
begin
  lValue := ceValue1; // doesn't work
  lValue := TCustomEnum.ceValue1; // works 
  lValue.AsString; // doesn't work
end;

person Triber    schedule 04.01.2018    source источник
comment
Нет, это не сработает.   -  person David Heffernan    schedule 04.01.2018
comment
Похоже, вы думаете, что можете наследовать тип enum. Вы не можете. Это работает только на уроках. Здесь вы определяете совершенно новый тип, который просто повторно использует существующий. Итак, теперь у вас есть два типа перечислений, каждый с одинаковым набором значений. Они не взаимозаменяемы, как вы хотите.   -  person Jerry Dodge    schedule 04.01.2018
comment
С другой стороны, какое отношение .AsString имеет к вашему вопросу? Это кажется совершенно не связанным. Может ли это быть потому, что в предыдущей строке отсутствует точка с запятой?   -  person Jerry Dodge    schedule 04.01.2018
comment
@JerryDodge Суть в том, чтобы предположить, что я не вижу вспомогательных методов, и я не хотел писать излишне длинный пример.   -  person Triber    schedule 04.01.2018
comment
@JerryDodge Это не новый тип. Это псевдоним. Есть тонкая разница.   -  person David Heffernan    schedule 04.01.2018


Ответы (1)


Ваши эксперименты дали именно то, что и следовало ожидать.

Единственная причина, по которой ваш третий модуль имеет доступ к TCustomEnum, заключается в том, что GroupUnit объявил псевдоним типа, используя тот же идентификатор, но только этот идентификатор. Важно отметить, что это важное отличие процесса компиляции Delphi от C++. В то время как C++ рекурсивно извлекает все включения в область компиляции, Delphi извлекает только раздел interface непосредственно включенных модулей.

  • lValue := ceValue1; не работает, потому что ceValue1 не определено в области компиляции.
  • lValue := TCustomEnum.ceValue1; works because TCustomEnum is pulled in in its entirety. That's to say because TCustomEnum is equal to BaseUnit.TCustomEnum: it means TCustomEnum.ceValue1 must be valid.
    1. You should note similar with objects and records. You can always reference public members, even if you only pull in the class/record type.
    2. Это отличается от предыдущего случая, потому что вы использовали ceValue1 без никакого уточнения. И нет однозначного определения для этого идентификатора в области видимости.
  • lValue.AsString; это не работает, потому что помощник, который делает AsString доступным, не входит в область действия.

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

Да, это уменьшает количество единиц, которые вы должны использовать, чтобы получить большое1 количество зависимостей. т.е. Вы думаете, что экономите время, заменяя uses Unit1, Unit2, Unit3, Unit4, Unit5, Unit6; более коротким uses GroupUnit;.

Но большие зависимости1 означают, что вы также добавили то, что вам не нужно. Это ведет к:

  • Плохо управляемая архитектура, где все косвенно зависит почти от всего остального. (Иначе известный как "Большой ком грязи" рекомендуемое исследование.)
  • Ненужные уровни косвенности; это означает, что если вы хотите найти определение того, что вы используете, вы Find declaration... только для того, чтобы добраться до псевдонима, и вам нужно Find declaration... снова найти то, что вы действительно хотите.

Да, кажется, больше работы, чтобы перечислить все, что вы используете. Но если вы используете так много вещей, что это вас расстраивает, вы должны спросить себя: Почему мой класс такой сложный?, а затем Что не так с моим дизайном; как его улучшить?


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

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

unit GroupUnit;

interface

uses BaseUnit;

type
  TCustomEnum = BaseUnit.TCustomEnum;
  TCustomEnumHelper = BaseUnit.TCustomEnumHelper;
const
  ceValue1 = BaseUnit.ceValue1;
  ceValue2 = BaseUnit.ceValue2;
  ceValue3 = BaseUnit.ceValue3;
person Disillusioned    schedule 04.01.2018
comment
Вы добавили верхний индекс 1 к термину «большой» в своем ответе. Теперь я ожидал сноски, объясняющей или ищущей это. Вы просто пропустили это или хотите указать на что-то другое? - person Sebastian Proske; 04.01.2018
comment
@SebastianProske В этом случае я дважды использовал large1, чтобы связать два экземпляра вместе; не для сноски. - person Disillusioned; 04.01.2018
comment
Этому надстрочному индексу действительно трудно следовать, и он противоречит интуиции для меня. Я никогда не видел ничего подобного раньше. Даже с вашим комментарием я все еще не понимаю, чего вы пытаетесь достичь. - person David Heffernan; 05.01.2018