Here is my shortened abstract class:
abstract class Report {
protected internal abstract string[] Headers { get; protected set; }
}
Here is a derived class:
class OnlineStatusReport : Report {
static string[] headers = new string[] {
"Time",
"Message"
}
protected internal override string[] Headers {
get { return headers; }
protected set { headers = value; }
}
internal OnlineStatusReport() {
Headers = headers;
}
}
The idea is, I want to be able to call Report.Headers
from anywhere in the assembly, but only allow it to be set by derived classes. I tried making Headers
just internal, but protected does not count as more restrictive than internal. Is there a way to make Headers internal and its set accessor protected AND internal?
I feel like I'm grossly misusing access modifiers, so any design help would be greatly appreciate.
What's wrong with making the getter public? If you declare the property as
public string[] Headers { get; protected set; }
it meets all of the criteria you want: all members of the assembly can get the property, and only derived classes can set it. Sure, classes outside the assembly can get the property too. So?
If you genuinely need to expose the property within your assembly but not publicly, another way to do it is to create a different property:
protected string[] Headers { get; set; }
internal string[] I_Headers { get { return Headers; } }
Sure, it's ugly decorating the name with that I_
prefix. But it's kind of a weird design. Doing some kind of name mangling on the internal property is a way of reminding yourself (or other developers) that the property they're using is unorthodox. Also, if you later decide that mixing accessibility like this is not really the right solution to your problem, you'll know which properties to fix.
It's not possible in C#.
Just for the sake of completeness, this is supported in IL (family and assembly access modifier).
I would keep the access modifier as protected and have an internal helper method.
protected override string[] Headers {
get { return headers; } // Note that get is protected
set { headers = value; }
}
internal SetHeadersInternal(string[] newHeaders)
{
headers = newHeaders;
}
But somehow, this smells like it should be refactored somehow. Internal is always something I'd use sparingly because it can lead to a very messy architecture where everything is somehow using everything else within the assembly, but of course there's always exceptions.
You could use an internal explicit implemented Interface:
internal interface IReport
{
string[] Headers { get; }
}
abstract class Report : IReport
{
protected abstract string[] Headers { get; protected set; }
string[] IReport.Headers
{
get { return Headers; }
}
}
class OnlineStatusReport : Report
{
static string[] headers = new string[] { "Time", "Message" };
protected internal override string[] Headers
{
get { return headers; }
protected set { headers = value; }
}
internal OnlineStatusReport()
{
Headers = headers;
}
}
Now you get internal access in the assembly where IReport is defined, which should be exactly what you want.
Implementing interfaces explicitly isn't a well known strategy, but it solves alot of problems.
The CLR supports the concept of protected AND internal (known as family-and-assembly accessibility) and C# SHOULD implemented/expose this concept. C# should probably allow the following:
internal string[] Header { get; protected set; }
Doing so should INTERSECT/AND both visibility modifiers for the property setter and allow you to read Headers from anywhere within the same assembly but only set it from derived classes within the same assembly.
It's a common belief that you cannot make some members both protected AND internal.
And its true that you cannot do so in a single line, as many, including myself, would wish, but with some cleverness it is 100% do-able.
//Code below is 100% tested
/* FROM ProtectedAndInternal.dll */
namespace ProtectedAndInternal
{
public class MyServiceImplementationBase
{
protected static class RelevantStrings
{
internal static string AppName = "Kickin' Code";
internal static string AppAuthor = "Scott Youngblut";
}
}
public class MyServiceImplementation : MyServiceImplementationBase
{
public void PrintProperties()
{
// WORKS PERFECTLY BECAUSE SAME ASSEMBLY!
Console.WriteLine(RelevantStrings.AppAuthor);
}
}
public class NotMyServiceImplementation
{
public void PrintProperties()
{
// FAILS - NOT THE CORRECT INHERITANCE CHAIN
// Error CS0122: 'ProtectedAndInternal.MyServiceImplementationBase.Relevant' is inaccessible due to its protection level
// Console.WriteLine(MyServiceImplementationBase.RelevantStrings.AppAuthor);
}
}
}
/* From AlternateAssemblyService.dll which references ProtectedAndInternal.dll */
namespace AlternateAssemblyService
{
public class MyServiceImplementation : MyServiceImplementationBase
{
public void PrintProperties()
{
// FAILS - NOT THE CORRECT ASSEMBLY
// Error CS0117: 'ProtectedAndInternal.MyServiceImplementationBase.RelevantStrings' does not contain a definition for 'AppAuthor'
// Console.WriteLine(RelevantStrings.AppAuthor);
}
}
}
Since C# 7.2 there is construct private protected
(link). It does not allow reading from the field (thus does not do exactly what the OP intends), but worth taking a loot.
来源:https://stackoverflow.com/questions/941104/how-to-make-a-property-protected-and-internal-in-c