【问题标题】:C# References; Keeping Members HiddenC# 参考资料;隐藏成员
【发布时间】:2024-01-07 21:36:01
【问题描述】:

假设您有一个如下定义的类。

public class SomeClass
{
     public Manager m { get; protected set; }
     public SpecialData data { get; protected set; }

     //More methods and member code here...
}

我需要我的经理类能够以某种方式更改 SpecialData 成员的集合引用。我可以使用 C++ 中的双指针或友元类来做到这一点,但遗憾的是,该选项在 C# 中不可用。如何保护 SpecialData 免受外部用户的设置,同时仍然允许 Manager 类控制设置?我可以使用 internal 关键字来做到这一点,但这似乎不太安全或干净......

非常感谢任何帮助。

【问题讨论】:

  • 您能解释一下为什么您认为internal 不安全或不干净吗?您的意思是一般情况下如此,还是您认为它不适合您的特定设计?我想说这是一个完美的解决方案,但说实话我不确定你为什么要首先这样做。

标签: c# oop friend double-pointer


【解决方案1】:

如何在Manager 类上创建一个事件,类似于RequestChangeSpecialData 事件。 Manager 触发事件,SomeClass 将更改 SpecialData 实例。

public class SomeClass
{
     private  Manager _m;

     public Manager M 
     { 
        get { return _m} 
        set 
        {
            // register/unregister event on property assignment
            if(_m != null)
                _m.RequestChangeSpecialData -= RequestChangeSpecialData;

            _m = value;

            if(_m != null)
                _m.RequestChangeSpecialData += RequestChangeSpecialData;

        }
     }

     public SpecialData Data { get; private set; }

     private void RequestChangeSpecialData(object sender, ChangeSpecialDataEventArgs e)
     {
        // set the new reference
        Data = e.SpecialData;
     }

}

public class Manager
{
    public void DoSomething()
    {
        // the manager class wants to do something, and wants to change the SpecialData instance.., so it fires the event RequestChangeSpecialData


        SpecialData data = new SpecialData();

        // if the event is bound.
        if(RequestChangeSpecialData != null)
            RequestChangeSpecialData(this, new ChangeSpecialDataEventArgs(data));
    }

    public event EventHandler<ChangeSpecialDataEventArgs> RequestChangeSpecialData;
}

public class ChangeSpecialDataEventArgs : EventArgs
{
    public SpecialData Data {get; private set; }

    public ChangeSpecialDataEventArgs(SpecialData Data)
    {
        Data = data;
    }
}

未测试(写在记事本中)

现在Manager 可以更改SpecialData 属性。这样管理器就不会依赖于SomeClass/接口或程序集。

【讨论】:

  • 即将发布活动选项
【解决方案2】:

创建一个继承 SomeClass 的类,看起来像这样:

internal class SomeClassDecorator : SomeClass
{
    public void SetSpecialData(SpecialData value)
    {
        data = value;
    }
}

Protected 意味着它对类的派生类可用,并且由于SomeClass 不是密封的,您可以简单地从它派生并做任何您需要的事情。然后你可以使用这个装饰器而不是 SomeClass 本身。此外,您可以拥有任意数量的装饰器,每个装饰器都处理其特殊的SpecialData

【讨论】:

    【解决方案3】:

    您可以将属性设为internal。这将使该属性仅在同一程序集中可见。或者,您可以使用InternalsVisibleToAttribute 来允许特定程序集访问该属性。

    另一种方法是使用interface 来隐藏该属性:

    public interface ISomeClass
    {
         Manager m { get; }
         SpecialData data { get; set; }
    }
    
    public class SomeClass : ISomeClass
    {
         public Manager m { get; protected set; }
         SpecialData ISomeClass.data { get; set; }
    
         //More methods and member code here...
    }
    

    这样,data 仅在接口引用中可见。

    所以这不起作用:

    SomeClass c = new SomeClass();
    c.data;
    

    但这确实:

    ISomeClass c = new SomeClass();
    c.data;
    

    【讨论】:

      【解决方案4】:

      它可能不是您正在寻找的,但 internal 描述的关键字 here 可以控制同一程序集中的访问;它似乎与 C++ 中的 friend 关键字类似。

      【讨论】:

        【解决方案5】:

        如果管理器类与SomeClass 属于同一程序集,则使成员internal 可以使同一程序集中的类可以访问设置器:

        public SpecialData data { get; protected internal set; }
        

        这类似于在 C++ 中使用friend,不同之处在于“友谊”扩展到同一程序集的所有成员。

        如果管理器是不同包的一部分,您可以在程序集上使用InternalsVisibleTo 属性。但是,在这种情况下,您应该签署您与之建立友谊的程序集,以避免尝试从未经授权的代码中获取对 setter 的访问权限。

        【讨论】:

        • 我确实提到了 internal 关键字。我意识到这是一种选择,但它是一个好的选择吗?我担心有人可能是设计缺陷。如果没有,这肯定会奏效。这是一个大项目,因此将它们结合在一起可能是一个大问题......
        • @user3812869 是的,internal 是一个不错的选择。当您设计一个共享内部表示知识的相关类包时,它就是为这种情况而设计的。 .NET 采取预防措施,不将这些知识泄露到您的库之外。据推测,没有办法保护您的代码免受可以修改您的程序集的人的侵害。
        • 好吧,在这种情况下,我会尝试解决问题。这个设计对我来说似乎有点奇怪。实际上,我有一个 StateManager 和一个包含在游戏对象中的 Sprite。 StateManager 包含状态。这些状态需要编辑 Sprite 字段。
        • @user3812869 最后一点听起来有点奇怪,因为听起来Sprite 是视觉表示的一部分,而其他一切都是模型的一部分。您最好更改各种精灵标识符,并将它们“链接”到视图级别的Sprite 对象。不过,这不会改变您在课程中设置可访问性的方式。