I’m designing a set of APIs for some applications I’m working on. I want to keep the code style consistent in all the classes I write but I’ve found that there are a few inconsistencies that I’m introducing and I don’t know what the best way to resolve them is.
My example here is specific to C# but this would apply to any language with similar mechanisms.
There are a few classes that I need for implementation purposes that I don’t necessarily want to expose in the API so I make them internal
whereever needed.
Generally what I would do is design the class as I normally would (e.g., make members public
/protected
/private
where necessary) and change the visibility level of the class itself to internal
. So I might have a few classes that look like this:
internal interface IMyItem
{
ItemSet AddTo(ItemSet set);
}
internal class _SmallItem : IMyItem
{
private readonly /* parameters */;
public _SmallItem(/* small item parameters */) { /* ... */ }
public ItemSet AddTo(ItemSet set) { /* ... */ }
}
internal abstract class _CompositeItem: IMyItem
{
private readonly /* parameters */;
public _CompositeItem(/* composite item parameters */) { /* ... */ }
public abstract object UsefulInformation { get; }
protected void HelperMethod(/* parameters */) { /* ... */ }
}
internal class _BigItem : _CompositeItem
{
private readonly /* parameters */;
public _BigItem(/* big item parameters */) { /* ... */ }
public override object UsefulInformation
{
get { /* ... */ }
}
public ItemSet AddTo(ItemSet set) { /* ... */ }
}
In another generated class (part of a parser/scanner), there is a structure that contains fields for all possible values it can represent. The class generated is internal
too but I have control over the visibility of the members and decided to make them internal
as well.
internal partial struct ValueType
{
internal string String;
internal ItemSet ItemSet;
internal IMyItem MyItem;
}
internal class TokenValue
{
internal static int EQ(ItemSetScanner scanner) { /* ... */ }
internal static int NAME(ItemSetScanner scanner, string value) { /* ... */ }
internal static int VALUE(ItemSetScanner scanner, string value) { /* ... */ }
//...
}
To me, this feels odd because the first set of classes, I didn’t necessarily have to make some members public
, they very well could have been made internal
. internal
members of an internal
type can only be accessed internally anyway so why make them public
? I just don’t like the idea that the way I write my classes has to change drastically (i.e., change all uses of public
to internal
) just because the class is internal
.
Any thoughts on what I should do here?
It makes sense to me that I might want to make some members of a class declared public
, internal
. But it’s less clear to me when the class is declared internal
.
2
As far as C# specification is concerned, there is no difference when you make a method internal
or public
so long as the type that holds it is internal
. The only consideration might be that if you were to make the class public in the future, whichever methods you are likely to make public
then, you could do that now. However, you may find differences of opinion even on this topic.
It comes to a matter of preference on your part (and your team) and I doubt that you may even get an agreement within your team if it’s more than a few developers, let alone on a community as broad as this.
In addition, these preferences work well for any new code being written, but for any existing code, there is no benefit to justify the cost of changing the code (except for developer’s own sanity). I have seen code written in both styles, even within the same project by two different developers.
As for the specification, pertinent parts of section 3.5.2 (p. 61) of C# 5.0 language specification states the following (and I believe it is the same in previous versions too):
The accessibility domain of a nested member M declared in a type T
within a program P is defined as follows (noting that M itself may
possibly be a type):
- If the declared accessibility of M is
public
, the accessibility domain of M is the accessibility domain of T.- If the declared accessibility of M is
internal
, the accessibility domain of M is the intersection of the accessibility domain of T with
the program text of P.
Since accessibility domain of T in your case is internal
, so accessibility domain of M is the same regardless of it being internal
or public
.
just make sure that in your given language there is no what I’ll call “side channel” through which consumers of your api could access a public member of an internal class, because if there is, and they find it and use it, that part of your code is “as good as” public api from then on. That means they scream if you change it and you get stuck supporting it.
In Java one example of a side channel is reflection. Devs can and have used it to gain access to members which the api author never intended they should know about.
1