In this post, I am going to show why I think the internal keyword, when put on class members, is a code smell and suggest better alternatives.
What is the internal keyword?
In C# the internal keyword can be used on a class or its members. It is one of the C# access modifiers. Internal types or members are accessible only within files in the same assembly.
Why we need the internal keyword?
“A common use of internal access is in component-based development because it enables a group of components to cooperate in a private manner without being exposed to the rest of the application code. For example, a framework for building graphical user interfaces could provide Control and Form classes that cooperate by using members with internal access. Since these members are internal, they are not exposed to code that is using the framework.” (C# internal keyword documentation)
These are the use cases I saw for using the internal keyword on a class member:
Call a class’s private function within the same assembly.
In order to test a private function, you can mark it as internal and exposed the dll to the test DLL via InternalsVisibleTo.
Both cases can be viewed as a code smell, saying that this private function should be public.
Let's see some examples
Here is a simple example. a function in one class wants to access a private function of another class.
The solution is simple — just mark A::func2 as public.
Let's look at a bit more complex example:
What’s the problem? just mark func2 as public as we did before.
But we can’t 😞. B is an internal class so it cannot be part of the signature of a public function of a public class.
Those are the solutions I found, ordered by easiness:
-
Mark the function with the internal keyword
-
Create an internal interface
-
Extract A.func2 to another internal class and use it instead of A.func2.
Decouple the function from internal classes and make it public. This is much dependant on what the function is doing with its inputs. decouple the internal classes can be very easy, very hard, and even impossible (without ruing the design).
But we don’t have public classes we use interfaces…
Let's look at some more real-world example:
Let's see how the previous solutions are adapted to this example:
-
Mark function with Internal. this means you will need to cast to class in order to call the function so This will work only if class A is the only one that implements the interface, meaning IA is not mocked in tests and there isn’t another production class that implements IA.
-
Create an internal interface that extends the public interface.
Extract A.func2 to another internal class and use it instead of A.func2.
Decouple the function from internal classes and add it to the public interface.
We can see that the internal keyword is the easiest solution, but there are other solutions using the traditional building blocks of OOP: classes and interfaces. We can see that the 2nd solution — adding an internal interface is not much harder than marking the function with the internal keyword.
Why not use the internal keyword?
As I showed in the previous examples, using the internal keyword is the easiest solution. But you are going to have a hard time in the future if you will need to:
Move the public class A to another DLL (since the internal keyword will no longer apply to the same dll)
Create another production class that implements IA
Mock IA in tests
You may think “But this is just one line of code, I or anyone else can change it easily if needed”. Now you have one line of code that looks like that:
((MyClass)a).internalFunction
but if others will need to call this function too this line will be copy-pasted around inside the DLL.
My Conclusion
I think marking a class member with the internal keyword is a code smell. In the examples I showed above it is the easiest solution, BUT can cause problems in the future. Creating an internal interface is almost as easy and more explicit.
Compare to C++
The C++ “friend” keyword is similar to the C# internal keyword. It allows a class or a function to access private members of a class. The difference is it allows access to specific class or function and not all the classes in the same DLL. In my opinion, this is a better solution than the C# internal keyword.
Top comments (0)