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

我的梦境 提交于 2019-11-26 17:55:17

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!