如何在 C# 中模拟友谊?

How to emulate friendship in C#?

我想声明(仅)class A 可以 access/manipulate class B 的(部分)成员。当没有 friend 存在于 C#?

其中一种可能性如下(希望它可以为某人节省一些思考)。这适用于以下人士:

  • 不喜欢(不想使用)internal or InternalsVisibleToAttribute.
  • 不喜欢(不想使用)反射来提取和利用 "internals"。

描述:

  • 让 类 AB 处理一些允许 A 操纵 B 的 "internals" 的协议(接口)。
  • B 定义非 public 方法来实现此协议(类似于标准接口实现,但使用 private/protected 方法 - 禁止标准用法interface 实施)。
  • B 创建一个专用对象,其中包含初始化为该协议成员的委托(有效导出选定的 "internals")。
  • 将此对象从 B 传递给 A(理想情况下不需要任何用户干预)以授予 A 操纵 B.[=70= 的权利]
  • A 中使用它,有效地允许 B 愿意提供的任何东西。

优点:

  • 减少 space 以减少 B 中任何意外的不需要的更改(相反的情况都是 internalpublic)。
  • 精确控制 AB 中可以完成的操作。

缺点:

  • 有点复杂(协议定义,需要协议传递过程)
  • A还提供了public基础方法(注册),调用不当可能会造成一些危害。

寻码者示例:

  • A是一个能修phone
  • 的技术员
  • Let B 是一个聪明的phone,它提供标准 (public) 功能,也作为一些进行基本维修的诀窍。
  • 用户可以使用标准功能,Technician(作为 SmartPhone's friend)可以使用高级功能。

    /*
     * SmartPhone<-Technician friendship contract
     */
    public class SmartPhoneServiceActivities
    {
        public delegate bool ReplaceGlassHandler  (SmartPhone device);
        public delegate bool ReplaceDisplayHandler(SmartPhone device);
    
        public ReplaceGlassHandler   ReplaceGlass {get;}
        public ReplaceDisplayHandler FactoryReset {get;}
    
        public SmartPhoneServiceActivities(ReplaceGlassHandler replaceGlass, ReplaceDisplayHandler factoryReset)
        {
            ReplaceGlass = replaceGlass;
            FactoryReset = factoryReset;
        }
    }
    
    //-------------------------------------------------------------------------------
    
    public class SmartPhone
    {
        #region Friend functions
        /*
         * Provide private functions for "trusted" classes
         */
        static SmartPhone()
        {
            var services = new SmartPhoneServiceActivities(ReplaceGlass, FactoryReset); // SmartPhone<-Technician agreement regarding "private modifications"
    
            Technician.RegisterDeviceForRepair(typeof(SmartPhone), services);
        }
    
        protected static bool ReplaceGlass(SmartPhone device)
        {
            device.IsDamagedGlass = false;
            return true; // Skilled technicians only ;)
        }
    
        protected static bool FactoryReset(SmartPhone device)
        {
            device.IsDamagedSoftware = false;
            return true;
        }
        #endregion
    
    
    
        #region Standard public usage
        public bool IsDamaged => IsDamagedGlass || IsDamagedSoftware;
        public bool IsDamagedGlass    {get; protected set;}
        public bool IsDamagedSoftware {get; protected set;}
    
        public void Call() {}
    
        public void DropIt()
        {
            var isBroken = (new Random((int)(DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds)).Next() & 3;
    
            IsDamagedGlass    = (isBroken & 1) != 0;
            IsDamagedSoftware = (isBroken & 2) != 0;
        }
        #endregion
    }
    
    // --------------------------------------------------------------------------------
    
    public class Technician
    {
        protected static readonly Dictionary<Type, SmartPhoneServiceActivities> knowHow = new Dictionary<Type, SmartPhoneServiceActivities>();
    
        /*
         * Although this is public and improper call might cause inability of a class to register it's acceptable "risk". 
         * It's intended for static constructors invocation.
         */
        public static void RegisterDeviceForRepair(Type deviceType, SmartPhoneServiceActivities services)
        {
            if (   (!knowHow.ContainsKey(deviceType))
                && (services != null))
            {
                knowHow[deviceType] = services;
            }
        }
    
        public bool Repair(SmartPhone device)
        {
            var isRepaired = false;
            var deviceType = device.GetType();
    
            if (knowHow.ContainsKey(deviceType))
            {
                var services  = knowHow[deviceType];
                var isSoftOk  = !device.IsDamagedSoftware || services.FactoryReset(device);
                var isGlassOk = !device.IsDamagedGlass    || services.ReplaceGlass(device);
    
                isRepaired = isSoftOk && isGlassOk;
            }
    
            return isRepaired; 
        }
    }
    

I would like to declare that only class A can access/manipulate some of members of class B.

突出显示的词表示有问题的地方。

首先,我假设 "only class A" 你实际上是指 "only class A and class B"。 class B 中声明的成员中的代码始终可以访问 class B 的所有成员,因此 class A 将 永远不会 成为 只有 class可以访问B的成员。

一种技术是使 class A 成为 class B 的唯一嵌套 class。现在 classes A 和 B 是唯一的 classes 可以访问 class B 的所有成员。但是,class A 可以访问 all class B 的成员;这不符合您的要求,即 class A 只能访问 class B.

成员的 some

另一种技术是使 class A 和 class B 都 class 在同一程序集中,而 classes 在程序集中。现在您可以将您希望 A 访问的 B 成员标记为 "internal",并且只有 class A 能够访问它们,因为它是唯一的其他 class组装.

可能这两种技术都不是您想要的。正如您所注意到的,C# 并未设计为具有 C++ 的 "friend" 功能。我的建议是让您希望从 A 访问的 B 成员成为内部成员。这使得当前程序集中的所有类型都可以访问它们,这些类型是由少数人编写的少量类型,而这些人都是您认识的。如果您有充分的、影响业务的理由强加此要求,您可以简单地要求他们不要使用 class B。

C# 类型系统的设计初衷并不是为了能够表示和加强同事之间所有可能的关系; "class B can be used by class A because I trust Albert, but I don't want Carol who works on class C to be able to re-use my work" 根本不是 C# 团队认为有足够价值可以添加到类型系统的功能。如果您对 Carol 使用 class B 有任何疑问,请与 Carol 联系。