如果您想将可用信息限制在Human,您可以选择多种方式。
-
如果您可以只隐藏信息,它仍然存在但无法访问,除非您明确取消隐藏它考虑使用接口来限制可用的成员。
-
如果您可以接受 Universe 的传递,但 Human 无法访问某些成员,请考虑使用 protected 修饰符来限制对从 Universe 类继承的成员的访问。
-
如果您可以在构造函数中传递东西(就像您推荐的那样),您可以将任意数量的方法(委托)传递给人类类,这样他们就可以在需要时随时获取信息,但这涉及更复杂的实现(我已经为您完成了下面的大部分工作)
-
如果您不确定自己想要做什么,并且这需要(对于某些特定的业务要求)以您描述的方式工作 - 考虑研究一般 Object Oriented Programing设计模式。网上有大量的资源可以教你 OOP。我推荐的主要主题是SOLID 原则,它会教给你很多东西并且非常有用。 感谢@flydog57 提到这一点,因为从长远来看这会更有用。
接口
要在视觉上隐藏/抽象信息,除非显式访问(强制转换),您可以实现一个 IUniverse 接口,该接口仅定义您希望公开访问的成员。
// these would be the only accessible members
public interface IUniverse
{
Animal FindAnAnimal();
void UniverseAction();
}
public class Universe : IUniverse { ... }
public class Human
{
private readonly IUniverse universe;
public Human(IUniverse universe)
{
this.universe = universe;
}
}
使用接口来抽象哪些信息应该可用,这真的很强大!但是,这并不妨碍Human 将IUniverse 显式转换为Universe 对象并访问它的其他公共成员。
受保护的修饰符
您可以使用protected 修饰符(和其他几个修饰符)完全删除对不满足某些要求的其他类的信息的访问。例如,protected 修饰符将禁止从任何不继承自Universe 的类访问任何protected 成员。请务必查看Access Modifiers,了解有关您可用的其他选项的更多信息。
public class Universe
{
protected List<Human> Humans { get; set; } = new();
protected List<Animal> Animals { get; set; } = new();
protected God AlphaOmega { get; set; }
public void UniverseAction()
{
//dosmthg
Console.WriteLine(nameof(UniverseAction));
}
public Animal FindAnAnimal()
{
//find an animal
Console.WriteLine(nameof(FindAnAnimal));
return Animals.FirstOrDefault();
}
}
public class Human
{
private readonly Universe universe;
public Human(Universe universe)
{
this.universe = universe;
}
//many Human things
public void CallingUniverseAction()
{
//How to?
universe.UniverseAction(); // works
UniverseAction.Humans.Clear(); // no access it's protected
}
public void CallingAnimalyStuff()
{
var animal = universe.FindAnAnimal(); // works
UniverseAction.Animals.Clear(); // no access it's protected
AlphaOmega.Kill(); // no access it's protected
}
}
通过代表
例如,您可以将委托传递给人类,以避免传递它自己的 Universe 实例。任何方法组通常都可以转换为某种形式的Action 或Func。请务必查看Actions 和Funcs,了解有关两者以及如何传递它们的更多信息。
你可以简单地传递这些 super 例如:
public class Universe
{
public Human CreateHuman()
{
var newHuman = new Human(UniverseAction, FindAnAnimal);
Humans.Add(newHuman);
return newHuman;
}
}
public class Human
{
private readonly Action universeAction;
private readonly Func<Animal> animalyStuff;
public Human(Action universeAction, Func<Animal> animalyStuff)
{
this.universeAction= universeAction;
this.animalyStuff = animalyStuff;
}
//many Human things
public void CallingUniverseAction()
{
//How to?
universeAction?.Invoke();
}
public void CallingAnimalyStuff()
{
var animal = animalyStuff?.Invoke();
}
}
如果您需要在构造函数(如 20+)中传递大量函数,您还可以实现更健壮但更复杂的系统。在构造函数中传递大量的东西不是可行的模式,但如果你真的想这样做,如果你需要这样做以与遗留系统互操作,它可以工作系统。
以下是使用反射的实现可能是什么样子的简短 sn-p。
public class Universe
{
protected List<Human> Humans { get; set; } = new();
protected List<Animal> Animals { get; set; } = new();
protected God AlphaOmega { get; set; }
public Human CreateHuman()
{
var newHuman = new Human(
(nameof(FindAnAnimal), (Func<Animal>)FindAnAnimal),
(nameof(UniverseAction), (Action)UniverseAction)
);
Humans.Add(newHuman);
return newHuman;
}
public void UniverseAction()
{
//dosmthg
}
public Animal FindAnAnimal()
{
//find an animal
}
}
public class Human
{
//many Human things
public void CallingUniverseAction()
{
Invoke(nameof(Universe.UniverseAction));
}
public void CallingAnimalyStuff()
{
var animal = Invoke(nameof(Universe.FindAnAnimal));
}
public Human(params (string Name, object Delegate)[] Methods)
{
foreach (var item in Methods)
{
InvokableReferences.Add(item.Name, item.Delegate);
}
}
private Dictionary<string, object> InvokableReferences = new();
public object Invoke(string DelegateName, params object[] Parameters)
{
if (InvokableReferences.ContainsKey(DelegateName))
{
object storedDelegate = InvokableReferences[DelegateName];
var delegateType = storedDelegate.GetType();
// check for the invoke method
var invokeMethod = delegateType.GetMethod(nameof(Invoke));
if (invokeMethod != null)
{
// check to see if it's an action or a func
var methodParams = invokeMethod.GetParameters();
if (methodParams is null)
{
// since there were no parameters then it is probably an Action or Func<T>
return invokeMethod.Invoke(storedDelegate, null);
}
// if it requires parameters it's probably a Action<T,..N> or Func<T...N,TResult>
// make sure we have enough parameters to invoke the method
if (methodParams.Length == Parameters.Length)
{
return invokeMethod.Invoke(storedDelegate, Parameters);
}
}
}
// if we failed to find the item return null;
return default;
}
}