【问题标题】:How to implement C++ like friend relationship in C# at Class level but not Assembly level?如何在类级别而不是程序集级别在 C# 中实现类似 C++ 的朋友关系?
【发布时间】:2014-09-22 01:53:50
【问题描述】:

我知道有一个“内部”关键字和 [InternalsVisibleTo] 属性。但是如何允许不在同一个程序集中的类级别的类修改私有数据呢?也就是说,只允许程序集中的特定类访问私有数据,而不允许该程序集下的每个类?

我之前在这里How to implement C++ like friend relationship in C#问过这个问题,但不够具体,所以我在这里再问一次。

这里讨论了与 C# 不使用朋友的理论和原因有关的其他讨论 Why does C# not provide the C++ style 'friend' keyword?

【问题讨论】:

  • 不要。想出更好的设计,谁需要朋友?这一直是 C++ 中代码臭味的标志。作为替代建议,也许考虑使用接口和显式实现msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx
  • @Mick 是的,我完全同意你的看法。但我只是出于好奇和学习/研究目的考虑它,只是想看看我们可以从 C# 中得到什么。
  • 我认为 Anders Hejlsberg 会充分了解 C++ 中可用的所有构造,并就如何帮助培养 C# 中的良好实践以及从 C# 中删除哪些 C++ 构造以消除不好的做法,C++ 朋友构造可能属于后一类。这是严重过度耦合设计的标志,当我说一个更好的设计时,我应该指定一个不那么高度耦合的设计。您在下面的回答是过度耦合的例证。
  • 看来您将自己的问题标记为骗子,而您所链接的问题的答案几乎与我上面所说的相呼应

标签: c# oop


【解决方案1】:

我已经考虑过了,我认为我有一个通过反思的解决方案。不确定这是不是一个好方法。

如果我的 SomeClass 有一个朋友 FriendClass。 NotFriendClass 和 FriendClass 在同一个程序集中,但只有 FriendClass 可以访问 SomeClass 的私有数据。这是 SomeClass 需要的:

class SomeClass
{
    private bool isMyFriend()
    {
        StackTrace st = new StackTrace();

        StackFrame callerSF = st.GetFrame(2);

        MethodBase mb = callerSF.GetMethod();
        Type callerType = mb.DeclaringType;

        // FriendClass is my friend
        if (typeof(FriendClass) == callerType)
            return true;
        else
            return false;

    }
// ....

在这个方法中 SomeClass 检查调用者类是否是他的朋友。是的,它有一个硬编码的 if (typeof(FriendClass) == callerType) 但 C++ 朋友还需要在声明中硬编码类名。

对于 SomeClass 的那些“朋友感知”方法,应该是这样的:

    public bool SetData(int x)
    {
        if (!isMyFriend())
           return false;

        this.privateData = x;
        return true;

    }

唯一的问题是它是运行时检查。但是,我仍然认为使用朋友将一些程序从 C++ 移植到 C# 已经足够了。

【讨论】:

    【解决方案2】:

    我通常将朋友关键字看起来很方便的情况转换为类本身决定哪些类可以实例化它的模式,而不会破坏正确的 OO 设计。

    这个想法是,想要控制谁可以实例化的类,为允许获取它的实例的类提供友谊。例如,InControl 类允许 ChosenOne 和 ChosenTwo 类获取 InControl 的实例。

    “选择”类(即 ChosenOne 或 ChosenTwo)可以“参与”由 InControl 提供的友谊与自身的实例(friendship.engage(this)),而友谊反过来可以通过提供该实例来“接受”该实例InControl 的一个实例 (friend.accept(new InControl()))。

    实际的友谊是通过嵌套的单实例友谊类实现的。 Friendship 仅限于使用通用 IFriendship 接口的“选择”类,其中 T 是选择类。反过来,每个“选择”类都需要实现通用 IFriendable 接口,其中 T 为 InControl,以便能够接收 InControl 的实例。

    InControl 类有一个私有构造函数来避免被除朋友以外的任何人实例化:

    public interface IFriendable<T>
    {
        void accept(T friend);
    }
    
    public interface IFriendship<T>
    {
        void engage(T friend);
    }
    
    public class ChosenOne : IFriendable<InControl>
    {
        private InControl _friend { get; set; }
    
        private ChosenOne()
        {
            InControl.friendship.engage(this);
        }
    
        public void accept(InControl friend)
        {
            _friend = friend;
        }
    }
    
    public class ChosenTwo : IFriendable<InControl>
    {
        private InControl _friend { get; set; }
    
        private ChosenTwo()
        {
            InControl.friendship.engage(this);
        }
    
        public void accept(InControl friend)
        {
            _friend = friend;
        }
    }
    
    public class InControl
    {
        public interface IFriendship : IFriendship<ChosenOne>, IFriendship<ChosenTwo> { }
    
        public static IFriendship friendship { get { return Friendship.instance; } }
    
        private class Friendship : IFriendship
        {
            static Friendship()
            {
            }
    
            internal static readonly Friendship instance = new Friendship();
    
            public void engage(ChosenOne friend)
            {
                friend.accept(new InControl());
            }
    
            public void engage(ChosenTwo friend)
            {
                friend.accept(new InControl());
            }
        }
    
        private InControl()
        {
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      相关资源
      最近更新 更多