How to make a property protected AND internal in C#?
Asked Answered
G

8

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.

Georgettegeorgi answered 2/6, 2009 at 18:22 Comment(9)
@Noldorin: protected internal is protected OR internal.Kassala
@Mehrdad: Yes, I knew that. What's the point?Sinusitis
@Noldorin: The point is, apparently, compiling fine is not what the OP wants...Kassala
"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.Sinusitis
@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.Kassala
@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?Sinusitis
@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.Kassala
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.Georgettegeorgi
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.Georgettegeorgi
H
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.

Hum answered 2/6, 2009 at 20:1 Comment(1)
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'Lunkhead
K
33

It's not possible in C#.

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

Kassala answered 2/6, 2009 at 18:26 Comment(1)
Are there any post-build-event tools that allow modifying IL code after compilation to get the desired result?Lunkhead
H
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.

Hum answered 2/6, 2009 at 20:1 Comment(1)
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'Lunkhead
B
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.

Bucket answered 2/6, 2009 at 18:30 Comment(1)
I like this way because it makes access within the assembly very explicit.Manado
A
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.

Aldose answered 11/10, 2011 at 14:9 Comment(0)
L
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.

Lunkhead answered 18/11, 2017 at 9:3 Comment(1)
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.Misogyny
H
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.

Homeless answered 6/12, 2012 at 22:22 Comment(2)
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.Homeless
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).Greenock
O
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);
        }
    }
}
Obvious answered 13/7, 2010 at 15:54 Comment(1)
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.Vagrom
H
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...

Handal answered 12/11, 2020 at 11:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.