C# 创建实例级接口的最佳方式

C# best way to create instance level interface

举个例子,假设我有一个带有一堆节点的有向无环图。我想遍历图,但是每个节点都有一个保护条件。例如,如果节点 A 的值小于 10,我只能遍历到节点 B。因此我创建了一个名为 IsActive 的方法,该方法是针对每个节点的。我尝试使用抽象 class 方法对其进行建模:

abstract class Node
{
    public int value;
    public abstract bool IsActive();
}

class NodeB : Node
{
    public override bool IsActive()
    {
        // return nodeA.value < 10
    }
}

但是,我想避免为每个节点创建一个新的 class。那么在实例级别 initialise/implement 方法的最佳方法是什么?我做了一些其他的事情,但我真的不确定“最好”的方法是什么...

谢谢!

你不需要很多 class。你可以简单地使用你的 Node class 但修改它。

public class Node
{
    public int Value { get; set; } = 0;
    public int LowerValueLimit { get; set; } = int.MinValue;
    public int UpperValueLimit { get; set; } = int.MaxValue;
    public bool IsActive()
    {
        return Value >= LowerValueLimit  && Value <= UpperValueLimit ;
    }
}

现在只需创建您的节点并在逻辑中烘焙它们

// is active will be true for anything between int.MinValue to 10
var nodeA = new Node() { UpperValueLimit = 10 }; 

 // will be active for value between 50 and 60
var nodeB = new Node()  { LowerValueLimit = 50, UpperValueLimit = 60 };

您可以将 Func<bool> 作为 属性 在您的 Node class:

public class Node
{
 public int Value {get;set;}
 public Func<bool> IsActiveFunction { get; set; }
 public bool IsActive()
 {
   return IsActiveFunction.Invoke();
 }
}

这样你就可以独立定义每个节点的条件:

Node nodeA = new Node();
Node nodeB = new Node();
nodeA.IsActiveFunction = () => nodeB.Value < 10;
nodeB.IsActiveFunction = () => Math.sin(nodeA.Value) == 0; // bad example, because you should not use == to compare doubles, but you get the idea

我会更进一步,创建可用于创建验证逻辑的接口

using System;
using System.Collections.Generic;
using System.Linq;

namespace NodeExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var graphRoot = new Node(1);

            var nodeA = new Node(7, new ValidateNodeBehaviour(currentNode => currentNode.Value >= 5));
            var nodeB = new Node(3, new ValidateNodeBehaviour(currentNode => currentNode.Value >= 10 && nodeA.Value <= 10));
            var nodeC = new Node(4);

            graphRoot.Connections.Add(nodeA);
            graphRoot.Connections.Add(nodeB);

            nodeB.Connections.Add(nodeC);

            //traverse the graph
            var currentNode = graphRoot;
            while (true)
            {
                var availableConnection = currentNode.Connections.FirstOrDefault(x => x.IsValid());

                if (availableConnection == null)
                    break;

                currentNode = availableConnection;
            }
        }
    }

    public interface IValidatableBehavior
    {
        public bool IsValid(Node currentNode);
    }

    public class ValidateNodeBehaviour : IValidatableBehavior
    {
        private Func<Node, bool> validateFunc;
        public ValidateNodeBehaviour(Func<Node, bool> validateFunc)
        {
            this.validateFunc = validateFunc ?? throw new ArgumentNullException(nameof(validateFunc));
        }

        public bool IsValid(Node currentNode)
        {
            return validateFunc(currentNode);
        }
    }

    public class Node
    {
        private readonly IValidatableBehavior validatableBehavior;

        public int Value { get; set; }
        public List<Node> Connections { get; } = new List<Node>();


        public Node(int value)
        {
            Value = value;
        }

        public Node(int value, IValidatableBehavior validatableBehavior) : this(value)
        {
            this.validatableBehavior = validatableBehavior;
        }

        public bool IsValid()
        {
            if (validatableBehavior == null)
            {
                return true;
            }

            return validatableBehavior.IsValid(this);
        }
    }
}