如何从 X 元素本身调用嵌入 List<X> 和 List<Y> 以及 Y 的类的方法?

问题描述

我虽然很容易找到这个问题的答案,但我还没有找到。

假设我有以下课程:

public class Universe
{
    public list<Human> Humans { get; set; }
    public list<Animal> Animals { get; set; }
    public God AlphaOmega { get; set; }
    
    public void UniverseAction()
    { 
        //dosmthg
    }

    public Animal FindAnAnimal()
    {
        //find an animal
    }
}

public class Animal
{
     //many Animal things
    public void AnimalyStuff()
    {
        //DoSmthg
    }
}

public class God
{
    public bool CantTouchThis = true;
}


public class Human
{
    //many Human things
    public void CallingUniverseAction()
    {
        //How to?
    }

    public void CallingAnimalyStuff()
    {
                  
    }
}

请注意,这些名称仅作为示例给出,我可以使用 A、B、C 和 D。

所以我希望所有人都能够从特定的 UniverseAction() 调用 Human 以便我可以使用它。 我还希望人类能够调用 AnimalyStuff(),因此特定的 Human 需要访问 FindAnAnimal 中的 Universe 以检索特定的 Animal 并执行AnimalyStuff()

有一段时间在处理这种需求时,我曾经在 Universe 的构造函数中传递 Human。虽然我不希望 Human 可以公开 Universe 的所有方法/参数。例如,Human 不应与 AlphaOmega

交互

最合适的方法是什么? 是通过我将在构造函数中传递的 action delegate 吗?如果是这样,我从未使用过动作委托。如果我想让 Human 访问很多方法,我最终不会传递很多委托吗?

解决方法

如果您想将可用信息限制为 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;
    }
}

使用接口来抽象哪些信息应该在哪些地方可用是非常强大的!但是,这并不能阻止 HumanIUniverse 显式转换为 Universe 对象并访问它的其他公共成员。

受保护的修饰符
您可以使用 protected 修饰符(和其他几个修饰符)完全删除对不满足某些要求的其他类的信息的访问。例如,protected 修饰符将禁止从任何继承自 protected 的类访问任何 Universe 成员。请务必查看 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 实例。任何方法组通常都可以转换为某种形式的 ActionFunc。请务必查看 ActionsFuncs 以获取有关两者以及如何传递它们的更多信息。

您可以简单地将这些传递给 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+),您还可以实现更健壮但更复杂的系统。在构造函数中传递大量内容不是一种可行的模式,但如果您真的想这样做,如果您要求这样做以与遗留系统互操作,它可以工作系统。

这里有一个简短的片段,展示了使用反射的实现可能是什么样子。

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;
    }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...