我想声明(仅)A类可以访问/操作B类成员。当friend
中没有C#
时,如何实现这一点? / p>
答案 0 :(得分:5)
我想宣布只有 A类可以访问/操纵 B类的某些成员。
突出显示的单词表示问题点。
首先,我假设只有A"只有A"你实际上是指"只有A级和B级"。在B类中声明的成员中的代码总是可以访问B类的所有成员,因此A类永远不会成为可以访问B成员的唯一类。
一种技术是使类A成为类B的唯一嵌套类。现在,类A和B是唯一可以访问类B的所有成员的类。但是,类A可以访问所有 B班成员;这不符合您的要求,即A类只能访问B类成员的部分。
另一种技术是将A类和B类都放在同一个程序集中的类中,并使程序集中的只类。现在你可以将你想要被A访问的B的成员标记为" internal",只有A类才能访问它们,因为它是程序集中唯一的其他类。
可能这些技术都不是你想要的。 C#并不是为了拥有这个朋友而设计的。正如你所注意到的,C ++的特性。我的建议是让你想要从A访问的B成员是内部成员。这使得当前程序集中的所有类型都可以访问它们,这是由少数人编写的少数类型,所有人都知道。你可以简单地要求他们不要使用B类,如果你有一些好的,有商业影响的理由来强加这个要求。
C#类型系统的设计并不能代表和执行同事之间的所有可能关系; " B级可以被A级使用,因为我相信Albert,但是我不想要在C级工作的Carol能够重复使用我的工作"这根本不是C#团队认为有价值的功能,可以添加到类型系统中。如果您对卡罗尔使用B级有疑虑,请与卡罗尔谈谈。
答案 1 :(得分:4)
其中一种可能性如下(希望它能为某人节省一些思考)。这适用于:
的人internal
或InternalsVisibleToAttribute
。说明
A
和B
处理一些允许A
操纵"内部" B
。B
定义实现此协议的非公开方法(类似于标准接口实现,但使用private
/ protected
方法 - 禁止标准使用interface
实现)。B
创建一个专用对象,其中包含初始化为此协议成员的代理(有效地导出所选的"内部")。B
传递到A
(理想情况下无需任何用户干预),以授予A
操纵B
的权利。A
中使用它,有效地允许B
愿意提供的内容。优点:
B
中任何意外不需要的更改空间的空间较小(与案例相反的是internal
或public
)。A
中B
可以完成的工作。缺点:
A
仍然提供公共基础设施方法(注册),不正当地称之为可能会造成一些伤害。寻求代码的示例:
A
是能够修理手机的技术人员B
是智能手机,提供标准(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;
}
}