如何在 C# 中使属性受保护和内部?

Posted

技术标签:

【中文标题】如何在 C# 中使属性受保护和内部?【英文标题】:How to make a property protected AND internal in C#? 【发布时间】:2010-10-30 18:40:38 【问题描述】:

这是我缩短的抽象类:

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 访问器受保护和内部的?

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

【问题讨论】:

@Noldorin:受保护的内部是受保护的或内部的。 @Mehrdad:是的,我知道。有什么意义? @Noldorin:关键是,显然,编译不是 OP 想要的...... "这个想法是,我希望能够从程序集中的任何地方调用 Report.Headers,但只允许它由派生类设置。" - 发布的代码不是这样吗?毕竟,也许我误解了它。 @Noldorin:他的意思是(据我所知)他希望只有在 两个 条件都为真时才能够访问该成员:该类是派生类并且它在同一个组件。也就是说,同一程序集中的非派生类无法访问它。 protected internal 无法实现。 【参考方案1】:

这在 C# 中是不可能的。

为了完整起见,IL(家族和程序集访问修饰符)支持这一点。

【讨论】:

是否有任何构建后事件工具允许在编译后修改 IL 代码以获得所需的结果?【参考方案2】:

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

public string[] Headers  get; protected set; 

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

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

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

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

【讨论】:

只有当protected属性的类型是public时才有效,比如string[]。如果 protected 属性的类型本身是 internal - 编译失败并显示消息 Inconsistent accessibility: property type 'Library.A' is less accessible than property 'Library.OnlineStatusReport.Headers'【参考方案3】:

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

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


internal SetHeadersInternal(string[] newHeaders)

    headers = newHeaders;

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

【讨论】:

【参考方案4】:

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

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 的程序集中获得内部访问权限,这应该正是您想要的。

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

【讨论】:

【参考方案5】:

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

【讨论】:

如果与internal 结合使用,它确实可以允许读取:internal override string[] Headers get return headers; private protected set headers = value; 将是仅限内部读取和仅限内部和受保护的写入。其他组合也是可能的。【参考方案6】:

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

internal string[] Header  get; protected set; 

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

【讨论】:

CLR 支持受保护和内部的概念(称为家族和程序集可访问性),我上面的回答表明 C# 应该实现/公开这个概念并解释建议的语法。请注意,我将 internal 修饰符放在属性上,将 protected 修饰符放在 setter 上(将“受保护的内部”放在 setter 上并不相同)。感谢您的评论,我已经编辑了我的答案以强调这个想法并避免未来的混乱。 它应该以某种方式支持......但我肯定不像它在这里......尝试建议内部和受保护的构造函数?只能从库(程序集)访问的构造函数,不应从其他库中的任何派生类型访问。也许我们可以区分“受保护的内部”和“内部受保护的”修饰符。所以第二个意味着它首先是“内部的”,然后才是“受保护的”。原始手段可用于任何派生类(“受保护”)以及内部任何地方(就像现在一样)。【参考方案7】:

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

确实,你不能在一行中做到这一点,包括我自己在内的许多人都希望这样做,但如果有点聪明,它是 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);
        
    

【讨论】:

内部类不必是静态的才能用于受保护的内部。我使用了你的想法,但在父类中,我有内部类的实例,用于访问我的“受保护的内部”成员。【参考方案8】:

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

internal bool _allowSetHeader = false;

protected void SetHeader(string[] newValue)

   if (_allowSetHeader)
   
     headers = newValue;
   

SetHeader 只能由派生类访问,除非 _allowSetHeader 设置为 true,否则它不会做任何事情,这只能由内部类完成...

【讨论】:

以上是关于如何在 C# 中使属性受保护和内部?的主要内容,如果未能解决你的问题,请参考以下文章

受保护的内部属性与受保护的属性和 Resharper

C# - 快速观察中的“可读”内部属性​​但不使用反射?

如何在 C# 中访问受保护的方法

Swig 类型映射内部构造函数到受保护

“受保护的内部”范围的目的是啥[重复]

ES6 类中的受保护属性(使用符号?)