0% found this document useful (0 votes)
4 views15 pages

Accessibility or Visibility Modifiers in C - Sharp

The document explains accessibility modifiers in C#, detailing how they control visibility of classes and their members. It covers six keywords: Public, Protected, Internal, Protected Internal, Private, and Private Protected, each with specific access rules and use cases. The document emphasizes the importance of these modifiers for security, maintainability, and design intent in programming.

Uploaded by

Daniel Solomon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views15 pages

Accessibility or Visibility Modifiers in C - Sharp

The document explains accessibility modifiers in C#, detailing how they control visibility of classes and their members. It covers six keywords: Public, Protected, Internal, Protected Internal, Private, and Private Protected, each with specific access rules and use cases. The document emphasizes the importance of these modifiers for security, maintainability, and design intent in programming.

Uploaded by

Daniel Solomon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 15

Accessibility or Visibility Modifiers in C_sharp

Article I: Foundations of Accessibility


Section 1 — What Is “Accessibility” Anyway?
1. What does “accessibility” mean in C#? I ask myself: at its core,
“accessibility” refers to who can see or use a given class, method,
property, field, or other member within code. If I mark something
as accessible, I’m effectively turning on a light in one part of my
program—allowing other parts to walk in and use it. If I mark it
inaccessible, I draw a privacy curtain.

2. Why do we need different levels?

o Security and correctness: I don’t want arbitrary code tinkering


with internal implementation details—like letting anyone directly
manipulate a bank account’s balance field.

o Maintainability: By carefully gating who can call or override


methods, I can change internals later without breaking external
consumers.

o Design intent: The compiler enforces my architectural vision


—“this bit is for everyone, that bit only for my subclasses,” and
so on.

3. Background Concepts Underpinning Accessibility

o Class and Member: A class is a blueprint; members are its


building blocks (fields, methods, properties, nested types,
events).

o Assembly: A compiled unit (DLL or EXE). Types in one assembly


can see some constructs in another, depending on accessibility.

o Derived Type: A class inheriting from another. Subclasses often


need to see some protected bits.

o Namespace vs. Assembly vs. Class: It’s tempting to confuse


“namespace” (logical grouping) with “assembly” (physical
grouping). Accessibility rules hinge on the assembly, not
namespace.

I must confront each of these as I dive into the specifics.


Article II: The Six Keywords Defined and Dissected
I start by listing the six access modifiers and then unpacking each, one at a
time, with surgical detail, analogies, and step-by-step inspection.

Keyword Accessibility
Public - Entire Program: Yes
- Containing Class: Yes
- Current Assembly: Yes
- Derived types: Yes
Protected - Entire Program: No
- Containing Class: Yes
- Current Assembly: No
- Derived types: Yes
Internal - Entire Program: No
- Containing Class: Yes
- Current Assembly: Yes
- Derived types: No
Protected - Entire Program: No
Internal
- Containing Class: Yes
- Current Assembly: Yes
- Derived types: Yes
Private - Entire Program: No
- Containing Class: Yes
- Current Assembly: No
- Derived types: No
Private - Entire Program: No
Protected
- Containing Class: Yes
- Current Assembly: No
- Derived types: No (unless same assembly and derived)

I note: “Protected Internal” is sometimes colloquially called “protected &


internal,” but its actual meaning is a union (accessible if either condition
holds). “Private Protected” is an intersection (accessible only if both
conditions hold), introduced in C# 7.2.
Section 1 — Public
1. Immediate Plain-English Translation “Public” means “any code
anywhere in this application can see and use me.” If I imagine my code
as a mansion, a public member is like the front door left unlocked for
everyone walking on the street.

2. Vocabulary Breakdown

o Entire Program (Yes): That means any class, any assembly—


regardless of which DLL or namespace—can consume this
member.

o Containing Class (Yes): Of course the class itself can use its
own public members.
o Current Assembly (Yes): Code in the same compiled DLL sees
it.

o Derived Types (Yes): Subclasses, even if in another assembly


or different namespace, see it.

3. Why Public?

o Library API: I’m writing a library (e.g., JsonSerializer in


System.Text.Json). I want consumers to call
JsonSerializer.Serialize(...); thus, Serialize must be public.

o Maximal Visibility: For top-level classes meant to be entry


points (e.g., Program class in a console app), Main is public
(though Main can be private for the runtime, but often public).

4. Edge Cases

o Public Fields? I hesitate: exposing fields publicly is usually


frowned upon—they break encapsulation. Rather, I’d expose a
public property. Still, C# allows public int Counter;, meaning
any code can read/write it.

o Public Nested Types: If I have a nested class marked public


inside a private class, is that nested class effectively
unreachable? Yes—because the containing class’s accessibility
gates it.

5. Analogies

o Imagine a public method like a public elevator in a building:


anyone from the lobby can hop in. No badge checks.
Section 2 — Protected
1. Plain-English “Protected” means “only my class and my subclasses
can see me, but no one else.” In mansion terms, this is like a private
staircase accessible only to family members and their children, not the
general public.

2. Vocabulary Breakdown

o Entire Program (No): Code in another, unrelated class cannot


see this member. If some random Logger class in a different
namespace/assembly tries instance.ProtectedMethod(), the
compiler complains.

o Containing Class (Yes): Of course, the class itself sees its


protected members.
o Current Assembly (No): Even if another class in the same DLL
tries to call it, they can’t—because “protected” doesn’t
automatically grant “assembly” access, only “class hierarchy”
access.

o Derived Types (Yes): This is the main point: subclasses


(whether in the same or different assembly) can call/override.

3. Why Protected?

o Polymorphism and Extensibility: I might have a base class


Animal with a protected method MakeSoundCore(). Then Dog :
Animal overrides MakeSoundCore(). Outside code calls
animal.MakeSound(), which delegates to MakeSoundCore(). By
keeping MakeSoundCore() protected, I hide implementation
details—only derived types know how to actually make the
sound.

o Control Over Inheritance: I can force derived classes to use


certain hooks (e.g., OnInitializing()) without letting outsiders
call those hooks directly.

4. Underlying Assumptions

o Subclasses Might Live in Other Assemblies: The moment I


allow someone to subclass Animal in a different DLL, they require
visibility of protected members.

o Protected vs. Internal: I note: “protected” does not include


“internal.” That is, just because two classes live in the same
assembly doesn’t grant protected access—there must be an
inheritance relationship.
5. Example Scenario

o I have public abstract class Shape { protected abstract


double ComputeArea(); public double Area() =>
ComputeArea(); }.

 ComputeArea() is hidden from outside; only concrete


subclasses (Circle, Rectangle) override and implement it.

 External code calls shape.Area(), but can’t directly call


ComputeArea().

6. Analogy

o A secret family book: only members of the family tree (the class
and subclasses) can read it. A cousin (unrelated class) in the
same town (assembly) can’t peek.

Section 3 — Internal
1. Plain-English “Internal” means “any code in the same assembly can
see me, but outsiders (other assemblies) cannot.” In mansion terms,
this is like an internal-only staff elevator—it’s open to employees who
wear the building badge (part of the same assembly), but guests can’t
access it.

2. Vocabulary Breakdown

o Entire Program (No): If assembly A compiles into Library.dll,


and assembly B references Library.dll, classes in B cannot see
internal members of classes in A.

o Containing Class (Yes): Naturally, the class itself sees its


internals.

o Current Assembly (Yes): This is the purpose: everything in


that DLL/EXE can see it.

o Derived Types (No): Subclasses in a different assembly do not


inherit accessibility of internal members—because internal does
not transcend assembly boundaries.

3. Why Internal?

o Encapsulation Within an Assembly: I might split a large


library into multiple source files, but I want helper
classes/methods to stay hidden from the consuming developer
who only sees the compiled DLL.
o Versioning and Maintenance: I can change or remove internal
members without worrying about breaking external consumers.

4. Edge Cases

o ‘InternalsVisibleTo’: A nuance: I can declare [assembly:


InternalsVisibleTo("FriendAssembly")]. Then FriendAssembly
sees my “internal” members. But that’s an explicit exception, not
the default.

o Internal Nested Types: If I make a nested class internal inside


a public class, any code in the same assembly sees it; outside,
it’s invisible—even if someone has a public reference to the outer
class.

5. Analogy

o A locked staff lounge: anyone wearing a staff ID card (code


compiled in the same assembly) can enter; external visitors
cannot.

Section 4 — Protected Internal


1. Plain-English This tricky beast combines “protected” OR “internal.” If
either condition is met—i.e., if I’m in the same assembly or I’m a
subclass—then I can see the member.

2. Vocabulary Breakdown

o Entire Program (No): Code in a completely different assembly


that neither inherits from the class nor is part of the same
assembly cannot see it.

o Containing Class (Yes): As always, the class itself can see its
own members.

o Current Assembly (Yes): Any code in the same DLL can


access.

o Derived Types (Yes): Even if a derived class lives in another


assembly, it can access.

3. Why Protected Internal?

o Flexible Sharing: I want my class’s subclassed


implementations to override or call certain methods, but I also
want helper classes in the same assembly (non-subclasses) to
access those members.
o Library Design: Often used in framework code: I mark a
method protected internal virtual void InitializeCore() so
that both derived implementations in other DLLs and internal
helpers in the same DLL can call or override it.

4. Deeper Reasoning

o I pause: Why “OR” and not “AND”? Because “AND” would be too
restrictive—“protected AND internal” means the subclass must
also live in the same assembly, which severely diminishes
extensibility across assemblies. Luckily, for that exact
intersection, C# provides “private protected”.

o So “protected internal” is a more permissive access: union of two


sets of eligible “callers.”

5. Analogy

o A VIP badge that works either if you’re a family member


(subclass) or if you’re a VIP guest inside the house (same
assembly)—either condition grants you access to the secret
lounge.

Section 5 — Private
1. Plain-English “Private” means “only the containing class (and its
nested types) can see me; everyone else—assembly mates and
subclasses alike—cannot.” In mansion terms, this is a hidden vault
sealed behind a door that only the room’s owner (the containing class)
can open.

2. Vocabulary Breakdown

o Entire Program (No): No external class, even in the same DLL,


can see private members.

o Containing Class (Yes): The class itself can access.

o Current Assembly (No): A sibling class in the same DLL cannot


peek.

o Derived Types (No): A subclass, even in the same assembly,


cannot access private members of its base class.

3. Why Private?

o Strict Encapsulation: I want to hide implementation details


even from potential subclasses—e.g., a sensitive helper method
ComputeChecksumPrivate() that no subclass should override or
call.

o Prevent Overriding: If a method is private, it can’t be


overridden in derived classes.

4. Edge Considerations

o Nested Types: If I have a nested class inside that class, the


nested type can see private members of its containing class. For
example:

public class Outer {


private int _secretValue;
private class Inner {
void Reveal() { Console.WriteLine(_secretValue); }
}
}

Here, Inner can see _secretValue.

5. Analogy

o A personal diary locked in a drawer: only the author (the class)


can read or modify it. No siblings (other classes) or children
(derived types) get to open it.

Section 6 — Private Protected


1. Plain-English “Private Protected” is the intersection (AND) of
“protected” and “internal.” Both conditions must be satisfied: I must
be a subclass and in the same assembly.

2. Vocabulary Breakdown

o Entire Program (No): As expected; arbitrary code doesn’t


qualify.

o Containing Class (Yes): The class itself naturally sees its


members.

o Current Assembly (No): Wait—I need to pause: “Current


Assembly (No)” seems contradictory, but actually, “private
protected” by definition is inaccessible to non-derived classes,
even if they’re in the same assembly. So despite being in the
same assembly, if I’m not a subclass, I cannot access.

o Derived Types (No): Also pause: “Derived types (No)” is true if


the derived type lives in another assembly. Only derived types in
the same assembly can see it. Because “protected” would grant
cross-assembly subclass access, but “private” restricts cross-
assembly. Meeting both means: same assembly AND subclass.

 Thus:

 A subclass in the same assembly: Yes.

 A subclass in another assembly: No.

3. Why Private Protected?

o Constrained Extensibility: I want certain members visible to


subclasses that reside in the same assembly (e.g., I’m designing
a framework package where I and my team create derived
classes all within the same assembly), but I don’t want external
subclassers to see them.

o Fine-Grained Encapsulation: It’s the rarest, most constrained


access: the intersection of internal boundaries and inheritance
boundaries.

4. Concrete Example

public class BaseWidget {


private protected void HelperOnlyForInternalDerived() {
/* ... */ }
}
// In same assembly:
public class DerivedWidget : BaseWidget {
void CallHelper() {
HelperOnlyForInternalDerived(); // Allowed
}
}
// In other assembly:
public class ExternalWidget : BaseWidget {
void TryCall() {
// HelperOnlyForInternalDerived(); // Error: inaccessible
}
}

o Notice: even though ExternalWidget is a derived type, it fails


because it’s not in the same assembly.
5. Analogy

o A secret office: you not only need a family connection (subclass)


but also must wear the building’s badge (same assembly). If you
fail either, the door stays locked.
Article III: The Full Accessibility Matrix
I restate the table with exhaustive commentary on each “Yes”/“No,”
ensuring no one wonders how I filled it out.

Entire Containing Current Derived


Keyword Program Class Assembly Types
Public Yes Yes Yes Yes
Protected No Yes No Yes
Internal No Yes Yes No
Protected No Yes Yes Yes
Internal
Private No Yes No No
Private No Yes No (since No (for
Protected non-derived derived
classes in outside
same assembly)
assembly are
excluded)
Yes (for
derived
types in
same
assembly)
1. “Entire Program: Yes/No” Column

o A “Yes” means literally any code anywhere (regardless of


assembly or inheritance) can see the member. Only public fulfills
this. Every other keyword is more restrictive.
2. “Containing Class: Yes” Column
o Unsurprisingly, every keyword allows the containing class to
access its own members. It’d be bizarre if a class could not see
its own private members.
3. “Current Assembly: Yes/No” Column

o Public: Yes (globals can see).

o Protected: No (assembly code is not “family” unless inherited).

o Internal: Yes (assembly code is “club” membership).

o Protected Internal: Yes (either protected OR internal; here,


internal condition suffices).

o Private: No (only the class, not siblings).


o Private Protected: No for non-derived types; but if derived in
the same assembly, that’s covered in the “Derived Types”
column rather than here.

4. “Derived Types: Yes/No” Column

o Public: Yes (any subclass, anywhere).

o Protected: Yes (explicitly for subclasses).

o Internal: No (subclasses outside the assembly cannot see


internals).

o Protected Internal: Yes (subclasses anywhere or siblings in the


same assembly).

o Private: No (subclasses cannot see privates).

o Private Protected: Yes, but only if the subclass is in the same


assembly. Subclasses outside the assembly do not meet the
“private” part of the condition, so they also see “No.”

Article IV: Deep Reasoning on Why These Levels


Exist
Section 1 — Historical and Design Motivations
I wonder: how did C# end up with six access modifiers—especially two weird
compound ones (“protected internal” and “private protected”)? Tracing the
lineage:
1. Early Object-Oriented Languages

o C++ had public, protected, and private—defining interface


vs. implementation vs. subclass hooks.

o When C# was designed, it inherited those, but also had the


concept of “assembly”—a new unit of distribution not present in
some languages. Thus, internal emerged to represent “within
this assembly.”

2. Protected Internal Introduction

o Developers needed a way to let both internal helpers and


subclass overrides see certain members without opening to the
entire world. Hence, “protected OR internal.”
3. Private Protected Addition (C# 7.2)
o People then noted a gap: “What if I want something visible only
to subclasses AND only to code in my assembly?” For example,
framework authors who build a suite of related classes in one
assembly want certain internals accessible to only that
assembly’s subclasses. So the “intersection” (AND) operator
emerged—“private protected.”
Section 2 — Impact on Design and Maintenance
1. Encapsulation Layers

o By carefully choosing one of these six, I can layer my code’s


architecture like geological strata—external façade (public),
friend zone (internal), family secret (protected), hybrid zones
(protected internal), locked vault (private), or a secret closet only
siblings who live under the same roof (private protected).
2. Versioning Safety

o If I expose something as internal, I know I can change it later


without worrying about breakage in consumer code. But if it’s
public, I must treat it as a committed part of my contract with
any user.
3. Testing and Unit Tests

o Often, unit tests live in a separate assembly. If I mark a method


internal, I can use [InternalsVisibleTo("MyTests")] to let tests
see it—otherwise, tests cannot call internal methods. But if I
marked it private, even tests can’t see it unless they’re nested
within the class, which they usually aren’t.

Article V: The Process of Choosing the “Right”


Modifier
I imagine: I’m building a class BankAccount. I have:
 A field _balance storing an integer.

 A method ComputeInterest().

 A method Deposit().

 A method Withdraw().

Let me walk through my thought process, step by indelible step:


1. Should _balance be private, protected, or public?
o If I make it public int _balance;, then any code can set
account._balance = -1000, bypassing deposit/withdraw rules.
That’s obviously dangerous.

o If I make it protected int _balance;, then subclasses (e.g.,


SavingsAccount or CheckingAccount) can see it, but any code in
the same assembly (like BankReporter class) cannot directly read
it—meaning BankReporter must call a public getter instead,
preserving encapsulation. That seems fine. But wait: sometimes I
want reporting tools in the same assembly to peek. Maybe I
prefer internal so that any class in the banking assembly can
see it? But then a subclass in another assembly (maybe a plugin)
couldn’t see it.

o Most likely: _balance is private. Then, I expose public read-only


property Balance or a protected property ProtectedBalance if
needed.

o I decide: _balance is private, because I want full control—no


subclass or assembly friend to muck with it.

2. What about ComputeInterest()?

o This is an internal calculation. I don’t want consumers (outside


the banking library) to call ComputeInterest() directly; instead,
they call ApplyInterest(), which in turn calls ComputeInterest().

o Do I want subclasses to override how interest is computed?


Possibly: a HighYieldAccount overrides ComputeInterest(). If so, I
mark protected virtual decimal ComputeInterest(). But now,
code elsewhere in the same library (e.g., InterestScheduler)
can’t call it directly—only via a protected internal or making a
public wrapper. Maybe I place it as protected internal virtual
decimal ComputeInterest(), allowing internal scheduler code to
call ComputeInterest(account) for any account, and also letting
subclasses override.

o I weigh: if I only ever override within the library’s subclasses,


private protected might suffice. But if I envision external
subclassers implementing their own accounts, I need plain
protected.

3. Should any method be internal?

o Perhaps there’s a helper class CurrencyConverter that I only want


visible in the banking assembly. Mark it internal. Then testing or
documentation can’t reference it in consumer code.
o But if I want unit tests in a separate test assembly to exercise
CurrencyConverter, I add [assembly:
InternalsVisibleTo("Banking.Tests")]. Now tests can see it, but
real consumers cannot.

Article VI: First-Person Doubts, Checks, and


Tangents
Section 1 — Tangent: Protected vs. Private Protected
I pause here: “Why not always use private protected for protected needs?”
My gut says: because “private protected” is more restrictive. If I mark a
member protected, any subclass—whether in the same or different assembly
—can use it. But if I mark it private protected, then subclasses in other
assemblies are locked out. So each has its place.
 If I want library authors outside my team to subclass and customize, I
choose protected.

 If I want only my internal team’s subclasses to have that power, I


choose private protected.

Section 2 — Tangent: Protected Internal vs. Internal


Protected?
I catch myself: “Did C# allow me to write internal protected? Or is it only
protected internal?” In fact, the order doesn’t matter—internal protected
compiles identically to protected internal. But by convention, most people
write protected internal, because the English reading “protected or
internal” flows better.
Yet, I wonder: “Is there a difference if I swap them?” I test mentally:
protected internal void Foo() { }
internal protected void Bar() { }

Both compile to the same IL metadata. So, convention matters more than
function.

Section 3 — What Happens If I Omit an Access Modifier?


By default, class members (fields, methods, properties) are private if I don’t
specify anything. Top-level classes default to internal. So:
 class MyClass { void DoSomething() { ... } } — here,
DoSomething() is implicitly private.
 class Helper { ... } (top-level) is implicitly internal, meaning no
other assembly sees Helper.

This default underscores the language’s favor toward encapsulation: you


must explicitly open up things.

Final Article: No Caller Unaware


I close by reasserting the only acceptable level of understanding:
I must be able to teach any developer—with no prior
knowledge of C# accessibility—exactly when and why each of
these six keywords applies, how they map to everyday
analogies, and how to choose them in real-world design.
If anyone reading this still wonders “Wait, can a subclass in another
assembly access a protected internal member?” or “Does private protected
allow same-assembly cousins to see it?” then I have failed. So, let me restate
the absolute essentials in crisp, plain bullets—no mind left behind:
 Public

o Anyone, anywhere—entire program, any assembly, any class,


any subclass—sees it.
 Protected

o Only the class itself and any subclass (in any assembly) sees it.
No one else, even if in the same assembly.
 Internal

o Any code in the same assembly sees it. Even non-derived classes
see it. But no one outside the assembly sees it.
 Protected Internal (“protected OR internal”)

o Any subclass (even in other assemblies) or any code in the same


assembly sees it. Only code that is neither a subclass nor in the
same assembly can’t see it.
 Private

o Only the containing class (and its nested types) sees it. No
siblings, no cousins, no outsiders.
 Private Protected (“protected AND internal”)

o Only subclasses in the same assembly see it. That’s the


intersection of “protected” and “internal.” All other cases—
siblings, external subclasses, random code—cannot.

You might also like