Dynamics 365. Изменения модификатора доступа Internal в X ++

В новом выпуске (версия 10.0.25/PU49), плановая дата выхода — апрель 2022 года, будут внесены некоторые изменения в работу модификатора доступа internal в X++.

Как вы, наверное, хорошо знаете, в X++ есть несколько модификаторов доступа, которые определяют видимость отдельных артефактов, таких как классы, методы и поля. Семантика большинства из них довольно очевидна и использовалась с начала эры объектно-ориентированного программирования, а именно: private, protected иpublic, но X++ (и в C#) также имеется модификатор internal. Артефакт с модификатором internal можно свободно использовать только в том же пакете, в котором он определен.

Были некоторые проблемы с применением компилятором X++ правил, касающихся internal, и теперь компилятор был исправлен, чтобы правильно диагностировать правила.

Почему мы вообще используем Internal?

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

Так что, с одной стороны, это здорово, потому что дает нам гораздо больше маневренности. С другой стороны, это может вызвать негодование разработчиков, которые хотят использовать внутренние API, т.к. теперь они уже не смогут этого делать — теперь внутренняя семантика internal реализована правильно. Маловероятно, что это серьезно повлияет на существующий клиентский код, но вероятность появления ошибок остается.

Какие изменения?

Посмотрим на них далее.

Public класс не может расширять internal класс в той же модели

Рассмотрим следующий код:

internal class MyClass {}

public class AnotherClass extends MyClass {}

Если оба класса находятся в одном модели, то ранее генерировалось следующее предупреждение:

Warning: Base class 'MyClass' is less accessible than class 'AnotherClass'

Теперь это ошибка:

InconsistentAccessibilityInheritance: Base class 'MyClass' is less accessible than class 'AnotherClass'

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

Расширение классов InternalUseOnly

Для таких случаев пока выдаются только предупреждения, которые не блокируют компиляцию стороннего кода. Следует начать планировать, чтобы не зависеть от артефактов с атрибутом internalUseOnly.

Внутренний класс не может использоваться, как внутренняя переменная и неверное сообщение об ошибке

Следующий код:

internal class ClassA {}

public class ClassB 
{
    internal ClassA a;
    public ClassA b;
    protected ClassA c;
    private ClassA d;
}

, где оба класса находятся в одном модели. Этот сценарий приводил к следующим ошибкам:

Error: InconsistentFieldAccessibility: The type 'ClassA' of the member variable 'a' is internal to Model 'MyModel' and is not accessible from model 'MyModel'.

Error: InconsistentFieldAccessibility: The type 'ClassA' of the member variable 'b' is internal to Model 'MyModel' and is not accessible from model 'MyModel'.

Error: InconsistentFieldAccessibility: The type 'ClassA' of the member variable 'c' is internal to Model 'MyModel' and is not accessible from model 'MyModel'.

Сейчас данный код приведет к следующим ошибкам:

Error:  InconsistentAccessibility: field type 'ClassA' is less accessible than field 'b.ClassB'

Error:  InconsistentAccessibility: field type 'ClassA' is less accessible than field 'c.ClassB'

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

Переопределение internal метода в той же модели и увеличение видимости не диагностируется как ошибка

class Base { internal void method() {} }

// в той же модели, как и класс Base:

class LocalA extends Base { public    void method() {} }

class LocalB extends Base { internal  void method() {} }

class LocalC extends Base { protected void method() {} }

class LocalD extends Base { private   void method() {} }

Этот код приводил к следующим ошибкам:

LocalC.xpp(3,5): OverrideMoreRestrictive: OverrideMoreRestrictive: Method 'LocalC.method' cannot have a more restrictive visibility than the method 'Base.method' which it overrides.

LocalD.xpp(3,5): OverrideMoreRestrictive: OverrideMoreRestrictive: Method 'LocalD.method' cannot have a more restrictive visibility than the method 'Base.method' which it overrides.

Теперь выдаются более качественные сообщения об ошибках:

LocalA.xpp(3,5):  CannotChangeAccess: 'LocalA.method' cannot change access modifiers when overriding inherited method 'Base.method'

LocalC.xpp(3,5):  CannotChangeAccess: 'LocalC.method' cannot change access modifiers when overriding inherited method 'Base.method'

LocalD.xpp(3,5):  CannotChangeAccess: 'LocalD.method' cannot change access modifiers when overriding inherited method 'Base.method'

Опять же, это влияет только на клиентские модели.

Следующий код:

class Base { internal void method() {} }

// in a different model than Base
class DerivedA extends Base { public void method() {} }
class DerivedB extends Base { internal void method() {} }
class DerivedC extends Base { protected void method() {} }
class DerivedD extends Base { private void method() {} }

Если упомянутые выше классы Base и Local* находятся в разных моделях, раньше были бы получены следующие ошибки:

DerivedB.xpp(3,5): InvalidOverrideIntenalMethod: Method 'method' in class 'Base' is internal and is not allowed to be overriden.

DerivedC.xpp(3,5): OverrideMoreRestrictive: OverrideMoreRestrictive: Method 'DerivedC.method' cannot have a more restrictive visibility than the method 'Base.method' which it overrides.

DerivedD.xpp(3,5): OverrideMoreRestrictive: OverrideMoreRestrictive: Method 'DerivedD.method' cannot have a more restrictive visibility than the method 'Base.method' which it overrides.

Теперь будут выданы следующие сообщения:

DerivedA.xpp(3,5):  CannotChangeAccess: 'DerivedA.method' cannot change access modifiers when overriding inherited method 'Base.method'

DerivedB.xpp(3,5):  InvalidOverrideIntenalMethod: Method 'method' in class 'Base' is internal and is not allowed to be overriden.

DerivedC.xpp(3,5):  CannotChangeAccess: 'DerivedC.method' cannot change access modifiers when overriding inherited method 'Base.method'

DerivedD.xpp(3,5):  CannotChangeAccess: 'DerivedD.method' cannot change access modifiers when overriding inherited method 'Base.method'

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

Другими словами, если модель Parent содержит класс:

internal class MyClass {} // В модели Parent

И модель Child (которая зависит от модели Parent) содержит:

public class AnotherClass extends MyClass {} // В модели Child

То произойдет ошибка компиляции, указывающая, что внутренний класс MyClass не виден в модели Child, так было всегда и это не изменилось.

Помимо ключевого слова internal, есть еще один способ пометить что-либо как внутреннее, использовать InternalUseOnly. Это не рекомендация к действию. Это значит, что артефакт является внутренним, как если бы было предоставлено ключевое слово internal. Отличается тем, что вместо соблюдения границ модели, в котором он определен, эти артефакты могут использоваться во всех моделях приложения. С внешней точки зрения это не имеет особого значения. Когда что-то является внутренним, это не должно использоваться извне. На текущий момент никаких изменений не вносилось в способ обработки артефактов internalUseOnly.

Источники

https://community.dynamics.com/365/financeandoperations/b/peter-s-x-developer-blog/posts/internal-fixes

www.codecrib.com/2021/11/changes-to-internal-access-modifier-in-xpp.html

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar