31

这是我缩短的抽象类:

abstract class Report {

    protected internal abstract string[] Headers { get; protected set; }
}

这是一个派生类:

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;
    }
}

这个想法是,我希望能够Report.Headers从程序集中的任何地方调用,但只允许它由派生类设置。我试着Headers只做内部的,但受保护的并不比内部更具限制性。有没有办法让 Headers 成为内部的,并且它的 set 访问器受保护和内部的?

我觉得我严重滥用了访问修饰符,所以任何设计帮助都将不胜感激。

4

8 回答 8

32

这在 C# 中是不可能的。

只是为了完整起见,这在 IL(家族和程序集访问修饰符)中得到支持。

于 2009-06-02T18:26:27.943 回答
19

公开吸气剂有什么问题?如果您将属性声明为

public string[] Headers { get; protected set; }

它满足您想要的所有标准:程序集的所有成员都可以获取该属性,并且只有派生类可以设置它。当然,程序集之外的类也可以获得该属性。所以?

如果您确实需要在程序集中但不公开公开该属性,另一种方法是创建一个不同的属性:

protected string[] Headers { get; set; }
internal string[] I_Headers { get { return Headers; } }

I_当然,用那个前缀装饰名字很难看。但这是一个奇怪的设计。对内部属性进行某种名称修饰是一种提醒自己(或其他开发人员)他们正在使用的属性是非正统的方式。此外,如果您后来决定像这样混合可访问性并不是解决问题的正确方法,您就会知道要修复哪些属性。

于 2009-06-02T20:01:24.417 回答
6

我会将访问修饰符保持为受保护的状态并具有内部辅助方法。

protected override string[] Headers {
    get { return headers; } // Note that get is protected
    set { headers = value; }
}

internal SetHeadersInternal(string[] newHeaders)
{
    headers = newHeaders;
}

但不知何故,这闻起来应该以某种方式重构。内部总是我会谨慎使用的东西,因为它会导致一个非常混乱的架构,其中所有东西都以某种方式使用程序集中的其他所有东西,但当然总是有例外。

于 2009-06-02T18:30:31.527 回答
6

您可以使用内部显式实现的接口:

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;
    }
}

现在您可以在定义 IReport 的程序集中获得内部访问权限,这应该正是您想要的。

显式实现接口并不是一个众所周知的策略,但它解决了很多问题。

于 2011-10-11T14:09:38.027 回答
5

从 C# 7.2 开始,就有了构造private protected链接)。它不允许从现场读取(因此不完全符合 OP 的意图),但值得一试。

于 2017-11-18T09:03:28.600 回答
4

CLR 支持受保护和内部的概念(称为家族和程序集可访问性),C# 应该实现/公开这个概念。C# 可能应该允许以下内容:

internal string[] Header { get; protected set; }

这样做应该 INTERSECT/AND 属性设置器的可见性修饰符,并允许您从同一程序集中的任何位置读取标题,但只能从同一程序集中的派生类中设置它。

于 2012-12-06T22:22:35.633 回答
2

人们普遍认为,您不能使某些成员既受保护又成为内部成员。

确实,你不能在一条线上这样做,包括我自己在内的许多人都希望这样做,但如果有一些聪明,它是 100% 可行的。

//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);
        }
    }
}
于 2010-07-13T15:54:01.977 回答
0

不是非常优雅但可行的解决方案,请明确实施:

internal bool _allowSetHeader = false;

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

SetHeader 只能由派生类访问,除非 _allowSetHeader 设置为 true,否则它什么也不做,这只能由内部类完成...

于 2020-11-12T11:01:14.633 回答