32

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.

Martin Backasch
  • 1,829
  • 3
  • 20
  • 30
xofz
  • 5,600
  • 6
  • 45
  • 63
  • 3
    @Noldorin: protected internal is protected OR internal. – Mehrdad Afshari Jun 02 '09 at 18:28
  • @Mehrdad: Yes, I knew that. What's the point? – Noldorin Jun 02 '09 at 18:30
  • 1
    @Noldorin: The point is, apparently, compiling fine is not what the OP wants... – Mehrdad Afshari Jun 02 '09 at 18:31
  • "The idea is, I want to be able to be able to call Report.Headers from anywhere in the assembly, but only allow it to be set by derived classes." - Is that not the case with the posted code? Maybe I'm misunderstanding it, after all. – Noldorin Jun 02 '09 at 18:33
  • @Noldorin: He means (as I understand) he wants to be able to access the member only if *both* conditions are true: The class is a derived class AND it's in the same assembly. That is, it's not accessible by non-derived classes in the same assembly. protected internal doesn't achieve it. – Mehrdad Afshari Jun 02 '09 at 18:35
  • @Mehrdad: Ok, so you *could* be right, though I've read and read the full post and think it's far from clear. If that is indeed what he wants, I would recommend against it purely from the design point of view - does he really need to do that? – Noldorin Jun 02 '09 at 18:38
  • @Noldorin: I think not, but I can't really be 100% sure. There are really cases where protected AND internal makes sense. Anyhow, I think (with the current design, at least), the posted code fails to achieve what the OP asks for. – Mehrdad Afshari Jun 02 '09 at 18:41
  • The code works, I'm just unhappy about having to use "protected internal". The only reason Headers can't be accessed outside the assembly by derived classes is simply because Report is internal. – xofz Jun 02 '09 at 18:43
  • It just seems like it would be better to have Headers be internal, while its set accessor could be 'proternal.' Then the access of Headers could be more easily and directly seen than having to glance at the access level of the abstract class. Err, I feel like I'm just whining now. Thanks everyone for your help and comments, and thanks for the article link Eric. – xofz Jun 02 '09 at 18:53

8 Answers8

33

It's not possible in C#.

Just for the sake of completeness, this is supported in IL (family and assembly access modifier).

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
19

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.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
  • 4
    It works only if the type of `protected` property is `public`, like `string[]`. If the type of `protected` property is itself `internal` - the compilation fails with message `Inconsistent accessibility: property type 'Library.A' is less accessible than property 'Library.OnlineStatusReport.Headers'` – Aleksei Poliakov Nov 29 '13 at 10:36
7

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.

Michael Stum
  • 177,530
  • 117
  • 400
  • 535
6

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.

LunicLynx
  • 939
  • 1
  • 8
  • 16
5

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.

Aleksei Poliakov
  • 1,322
  • 1
  • 14
  • 27
  • It could indeed allow reading if used in combination with `internal`: `internal override string[] Headers { get { return headers; } private protected set { headers = value; } }` would be internal-only read and internal-and-protected-only write. Other combinations would be possible too. – Jon Hanna Nov 27 '17 at 15:47
4

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.

Jorge Garcia
  • 2,042
  • 23
  • 25
  • The CLR supports the concept of protected AND internal (known as family-and-assembly accessibility) and my answer above suggests that C# SHOULD implemented/expose this concept and interpret the proposed syntax as such. Notice I put the internal modifier on the property and the protected modifier on the setter (not the same at putting both "protected internal" on the setter). Thanks for commenting, I have edited my answer to emphasize this idea and avoid future confusions. – Jorge Garcia Oct 11 '13 at 15:40
  • It should support in some way... but I am sure not like it is here... try to suggest internal AND protected constructor? constructor which should be accessed only from library (assembly) and should not be accessed from any derived type in other libraries. Maybe we can differ "protected internal" and "internal protected" modifiers. So 2nd one means that it is "internal" first and only then "protected". And original means available in any derived class ("protected") and as well everywhere internally (as it is now). – Maxim Feb 15 '16 at 02:25
2

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);
        }
    }
}
leat
  • 1,418
  • 1
  • 15
  • 21
  • The inner class doesn't have to be static to be used for protected internal. I used your idea, but in the parent class, I had instances of the inner class that I used to access my "protected internal" members. – Thick_propheT Jun 30 '14 at 16:49
0

Not perfectly elegant but a workable solution, implement it explicitly :

internal bool _allowSetHeader = false;

protected void SetHeader(string[] newValue)
{
   if (_allowSetHeader)
   {
     headers = newValue;
   }
}

SetHeader can only be accessed by a derived class and it will do nothing unless _allowSetHeader is set true which can only be done by an internal class...