I'm Joris "Interface" de Gruyter. Welcome To My

Code Crib

Changes To Internal Access modifier in X++

Nov 22, 2021
Filed under: #daxmusings #bizapps

In the upcoming April 2022 release (version 10.0.25/PU49), some changes will be introduced to how the internal access modifier works in X++. Although still not as strict as C#, it will at least fix a few issues with how it works. Note that the InternalUseOnly attribute is a different feature altogether, and still only generates warnings.

Before we dive into the changes, let’s understand why and when internal is used. Essentially, marking something as internal allows only the code in the same assembly to access it. In X++, an assembly is a package so only models in that package can access the internal code. The reason to use this isn’t much different from using private methods or members, except the allowed scope to use the code is the whole assembly and not just the class. In the context of Finance and Operations apps and X++, marking code as internal allows engineers to build the code but give themselves the option of changing the signatures of their code without worrying of breaking any code that extends it.

Note that the InternalUseOnly attribute is a different feature altogether, and still only generates warnings.

Summary

  • If you make use of the internal access modifier in your own code (declare methods or classes internal) you likely want to review the details below and you may have to fix some issues in your code. Since you’re in full control of your own code, this should not present any problems.
  • For all other code, the only potential issue happens when your code is using INHERITANCE on a Microsoft class and the code is trying to override an internal method and/or changing its access modifier.
  • All code with InternalUseOnly attributes is still only giving warnings.

Details

I’d like to thank the team and specifically Laurent for not only working on these compiler issues, but providing the following detailed examples of the changes.

The issues with internal that were corrected, are largely around how inheritance works and what the compiler allows or does not allow you to do. Let’s go right into the details. I encourage you to also read Peter Villadsen’s blog post on the matter.

A public class shouldn’t be allowed to extend an internal class

internal class ClassA {}
public class ClassB extends classA {} // classB is in the same model as classA

This scenario was changed from a warning to an error (the message remaining the same: InconsistentAccessibilityInheritance: Base class 'ClassA' is less accessible than class 'ClassB').

Since customer code cannot extend our internal classes to begin with, this will not impact customer code that extends Microsoft code. Customer code will be impacted if it declares its own internal class and publicly derives from it.

An internal class couldn’t be used as an internal member + misleading message

internal class ClassA {}
public class ClassB // ClassB is in the same model as ClassA
{
        internal ClassA a;
        public ClassA b;
        protected ClassA c;
        private ClassA d;
}

This scenario used to result in the following errors:
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'.

With the change, this results in the following errors:
Error: InconsistentAccessibility: field type 'ClassA' is less accessible than field 'b.ClassB'
Error: InconsistentAccessibility: field type 'ClassA' is less accessible than field 'c.ClassB'

This is mostly LESS restrictive than before. Customers COULD be impacted if their code is exploiting the compiler issue where private use of non-accessible internal types was allowed.

Overriding an internal method in the same package and increasing visibility was not diagnosed as an error

class Base { internal void method() {} }

// in the same model as 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() {} }

This resulted in the following errors:
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.

This will now result in the following errors:
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'

Customer code will only be impacted if in their own model they have an internal method and override it publicly.

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() {} }

This used to result in the following errors:
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.

It is now diagnosed as:
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'

Customer code will only be impacted if it is exploiting the existing gap in the compiler where publicly overriding a non-accessible internal method wasn’t diagnosed.

 

There is no comment section here, but I would love to hear your thoughts! Get in touch!

Blog Links

Blog Post Collections

Recent Posts