C#和Unity中的解释器模式使用方式
目录
- 概述
- 一、解释器模式的核心概念
- 1. 解释器模式的主要角色
- 2. 解释器模式的 UML 类图
- 二、解释器模式的实现方式
- 1. 基础实现(布尔表达式解释器)
- 2. 数学表达式解释器(四则运算)
- 三、解释器模式的特点
- 1. 优点
- 2. 缺点
- 四、解释器模式的使用场景
- 1. 典型应用场景
- 2. 具体案例
- 五、解释器模式的进阶话题
- 1. 语法树构建
- 2. 使用访问者模式遍历语法树
- 3. 解释器模式与编译器技术的区别
- 六、解释器模式的最佳实践
- 七、解释器模式与其他模式的关系
- 八、现代替代方案
- 九、在Uniry中的应用
- 示例1:简单数学表达式解释器
- 示例2:简单AI行为脚本解释器
- 示例3:对话条件解释器
- 在Unity中的实现建议
- 总结
概述
解释器模式(Interpreter Pattern)是一种行为型设计模式,它定义了一种语言的文法表示,并提供一个解释器来解释该语言中的句子。
这种模式主要用于处理特定类型的问题,特别是那些可以被表示为语言句子的领域。
一、解释器模式的核心概念
1. 解释器模式的主要角色
AbstractExpression(抽象表达式) :
- 声明一个抽象的解释操作接口
TerminalExpression(终结符表达式) :
- 实现与文法中的终结符相关联的解释操作
- 句子中的每个终结符都需要一个实例
NonterminalExpression(非终结符表达式) :
- 文法中的每条规则都需要一个非终结符表达式类
- 包含对其他表达式的引用(可能是终结符或非终结符)
Context(上下文) :
- 包含解释器之外的全局信息
Client(客户端) :
- 构建(或被给定)表示该语言中特定句子的抽象语法树
- 调用解释操作
2. 解释器模式的 UML 类图
二、解释器模式的实现方式
1. 基础实现(布尔表达式解释器)
// 上下文 - 存储变量值 public class Context { private readonly Dictionary<string, bool> _variables = new(); public bool GetVariable(string name) => _variables.TryGetValue(name, out var value) ? value : false; public void SetVariable(string name, bool value) => _variables[name] = value; } // 抽象表达式 public interface IExpression { bool Interpret(Context context); } // 终结符表达式 - 变量 public class VariableExpression : IExpression { private readonly string _name; public VariableExpression(string name) => _name = name; public bool Interpret(Context context) => context.GetVariable(_name); } // 非终结符表达式 - AND public class AndExpression : IExpression { private readonly IExpression _left; private readonly IExpression _right; public AndExpression(IExpression left, IExpression right) { _left = left; _right = right; } public bool Interpret(Context context) => _lefandroidt.Interpret(context) && _right.Interpret(context); } // 非终结符表达式 - OR public class OrExpression : IExpression { private readonly IExpression _left; private readonly IExpression _right; public OrExpression(IExpression left, IExpression right) { _left = left; _right = right; } public bool Interpret(Context context) => _left.Interpret(context) || _right.Interpret(context); } // 非终结符表达式 - NOT public class NotExpression : IExpression { private readonly IExpression _expression; public NotExpression(IExpression expression) => _expression = expression; public bool Interpret(Context context) => !_expression.Interpret(context); } // 客户端代码 class Program { static void Main() { // 创建上下文并设置变量 var context = new Context(); context.SetVariable("A", true); context.SetVariable("B", false); context.SetVariable("C", true); // 构建表达式: (A AND B) OR (NOT C) var expression = new OrExpression( new AndExpression( new VariableExpression("A"), new VariableExpression("B")), new NotExpression( new VariableExpression("C"))); // 解释执行 bool result = expression.Interpret(context); Console.WriteLine($"表达式结果为: {result}"); // 输出 False } }
2. 数学表达式解释器(四则运算)
// 上下文 - 存储变量值 public class MathContext { private readonly Dictionary<string, int> _variables = new(); public int GetVariable(string name) => _variables.TryGetValue(name, out var value) ? value : 0; public void SetVariable(string name, int value) => _variables[name] = value; } // 抽象表达式 public interface IMathExpression { int Interpret(MathContext context); } // 终结符表达式 - 数字 public class NumberExpression : IMathExpression { private readonly int _number; public NumberExpression(int number) => _number = number; public int Interpret(MathContext context) => _number; } // 终结符表达式 - 变量 public class VariableMathExpression : IMathExpression { private readonly string _name; public VariableMathExpression(string name) => _name = name; public int Interpret(MathContext context) => context.GetVariable(_name); } // 非终结符表达式 - 加法 public class AddExpression : IMathExpression { private readonly IMathExpression _left; private readonly IMathExpression _right; public AddExpression(IMathExpression left, IMathExpression right) { _left = left; _right = right; } public int Interpret(MathContext context) => _left.Interpret(context) + _right.Interpret(context); } // 非终结符表达式 - 减法 public class SubtractExpression : IMathExpression { private readonly IMathExpression _left; private readonly IMathExpression _right; public SubtractExpression(IMathExpression left, IMathExpression right) { _left = left; _right = right; } public int Interpret(MathContext context) => _left.Interpret(context) - _right.Interpret(context); } // 使用示例 var context = new MathContext(); context.SetVariable("x", 10); context.SetVariable("y", 5); // 构建表达式: (x + 5) - (y - 2) var expression = new SubtractExpression( new AddExpression( new VariableMathExpression("x"), new NumberExpression(5)), new SubtractExpression( new VariableMathExpression("y"), new NumberExpression(2))); int result = expression.Interpret(context); // 结果为 12
三、解释器模式的特点
1. 优点
- 易于扩展语法:新增表达式类即可扩展语言
- 易于实现简单语言:对于简单文法实现直观
- 分离语法分析:将语编程客栈法分析与表达式执行分离
- 灵活性强:可以动态改变解释方式
2. 缺点
- 复杂度高:对于复杂文法,类数量会急剧增加
- 效率较低:解释器模式通常比编译器效率低
- 难以维护复杂文法:文法规则过多时代码难以维护
- 应用场景有限:仅适用于特定领域问题
四、解释器模式的使用场景
1. 典型应用场景
领域特定语言(DSL) :
- 正则表达式解释器
- SQL条件解释器
- 业务规则引擎
数学表达式处理:
- 科学计算器
- 公式编辑器
- 财务计算系统
配置文件解析:
- 自定义配置语法
- 过滤条件解析
编译器/解释器:
- 简单编程语言解释器
- 模板引擎
游戏开发:
- 游戏AI行为脚本
- 技能效果描述语言
2. 具体案例
案例1:正则表达式解释器(简化版)
// 抽象表达式 public interface IRegexExpression { bool Interpret(string input); } // 终结符表达式 - 字符匹配 public class CharExpression : IRegexExpression { private readonly char _char; public CharExpression(char c) => _char = c; public bool Interpret(string input) => input.Length > 0 && input[0] == _char; } // 非终结符表达式 - 序列 public class SequenceExpression : IRegexExpression { private readonly List<IRegexExpression> _expressionsphp; public SequenceExpression(params IRegexExpression[] expressions) => _expressions = new List<IRegexExpression>(expressions); public bool Interpret(string input) { string remaining = input; foreach (var expr in _expressions) { if (!expr.Interpret(remaining)) return false; remaining = remaining.Substring(1); } return true; } } // 非终结符表达式 - 或 public class OrExpression : IRegexExpression { private readonly IRegexExpression _left; private readonly IRegexExpression _right; public OrExpression(IRegexExpression left, IRegexExpression right) { _left = left; _right = right; } public bool Interpret(string input) => _left.Interpret(input) || _right.Interpret(input); } // 使用 var regex = new SequenceExpression( new CharExpression('a'), new OrExpression( new CharExpression('b'), new CharExpression('c'))); bool match1 = regex.Interpret("ab"); // true bool match2 = regex.Interpret("ac"); // true bool match2 = regex.Interpret("ad"); // false
案例2:业务规则引擎
// 业务规则上下文 public class BusinessContext { js public Dictionary<string, object> Data { get; } = new(); } // 条件表达式 public class ConditionExpression { private readonly string _field; private readonly object _value; private readonly string _operator; public ConditionExpression(string field, string op, object value) { _field = field; _operator = op; _value = value; } public bool Interpret(BusinessContext context) { if (!context.Data.TryGetValue(_field, out var fieldValue)) return false; return _operator switch { "==" => Equals(fieldValue, _value), ">" => Comparer.Default.Compare(fieldValue, _value) > 0, "<" => Comparer.Default.Compare(fieldValue, _value) < 0, _ => false }; } } // 规则集 public class RuleSet { private readonly List<ConditionExpression> _conditions = new(); public void AddCondition(ConditionExpression condition) => _conditions.Add(condition); public bool EvaLuate(BusinessContext context) { return _conditions.All(c => c.Interpret(context)); } } // 使用 var context = new BusinessContext(); context.Data["Age"] = 25; context.Data["Salary"] = 50000; context.Data["IsEmployed"] = true; var rule = new RuleSet(); rule.AddCondition(new ConditionExpression("Age", ">", 18)); rule.AddCondition(new ConditionExpression("Salary", ">", 40000)); rule.AddCondition(new ConditionExpression("IsEmployed", "==", true)); bool eligible = rule.Evaluate(context); // true
五、解释器模式的进阶话题
1. 语法树构建
通常需要配合解析器将输入文本转换为抽象语法树(AST):
public class Parser { public IExpression Parse(string input) { // 简单实现 - 实际需要更复杂的词法/语法分析 if (input.Contains("AND")) { var parts = input.Split(new[] {" AND "}, StringSplitOptions.None); return new AndExpression(Parse(parts[0]), Parse(parts[1])); } else if (input.Contains("OR")) { var parts = input.Split(new[] {" OR "}, StringSplitOptions.None); return new OrExpression(Parse(parts[0]), Parse(parts[1])); } else { return new VariableExpression(input.Trim()); } } } // 使用 var parser = new Parser(); var expression = parser.Parse("A AND B OR C");
2. 使用访问者模式遍历语法树
public interface IExpressionVisitor { void Visit(VariableExpression exp); void Visit(AndExpression exp); void Visit(OrExpression exp); } public class PrintVisitor : IExpressionVisitor { public void Visit(VariableExpression exp) => Console.Write(exp.Name); public void Visit(AndExpression exp) { Console.Write("("); exp.Left.Accept(this); Console.Write(" AND "); exp.Right.Accept(this); Console.Write(")"); } public void Visit(OrExpression exp) { Console.Write("("); exp.Left.Accept(this); Console.Write(" OR "); exp.Right.Accept(this); Console.Write(")"); } } // 在表达式接口中添加Accept方法 public interface IExpression { bool Interpret(Context context); void Accept(IExpressionVisitor visitor); }
3. 解释器模式与编译器技术的区别
特性 | 解释器模式 | 编译器 |
---|---|---|
执行方式 | 直接执行语法树 | 生成中间代码/机器码 |
效率 | 较低(每次解释) | 较高(预先编译) |
灵活性 | 高(可动态修改) | 低(编译后固定) |
实现复杂度 | 相对简单 | 复杂 |
适用场景 | 简单DSL、动态需求 | 通用编程语言 |
六、解释器模式的最佳实践
控制文法复杂度:
- 解释器模式适合相对简单的文法(BNF范式不超过一页)
- 复杂文法考虑使用解析器生成器(如ANTLR)
共享终结符:
- 终结符表达式通常是无状态的,可以共享实例
分离解析与解释:
- 使用单独解析器构建语法树
- 保持解释器专注于执行
考虑性能优化:
- 缓存解释结果
- 预编译常用表达式
合理使用组合:
- 与访问者模式结合遍历语法树
- 与享元模式共享终结符
七、解释器模式与其他模式的关系
与组合模式:
- 抽象语法树就是组合模式的应用
- 非终结符表达式是组合节点,终结符表达式是叶节点
与访问者模式:
- 访问者模式可用于在语法树上执行多种操作
- 分离解释逻辑与语法树结构
与享元模式:
- 共享终结符表达式实例
- 减少内存使用
与策略模式:
- 解释器模式可以看作是在语法树上应用的策略模式
八、现代替代方案
对于复杂语言处理,现代开发中更常用:
解析器生成器:
- ANTLR
- Yacc/Lex
表达式树:
- C#的
Expression<T>
- 动态构建和执行表达式
脚本引擎:
- Roslyn脚本API
- Lua、python等嵌入式脚本
总结一下:
解释器模式在C#中适用于:
- 特定领域语言:需要为特定领域定义简单语言
- 灵活规则系统:业务规则需要动态配置
- 数学表达式:需要解释执行公式
关键优势:
✅ 易于实现简单语言的解释执行
✅ 灵活扩展语法规则
✅ 分离语法定义与执行
适用限制:
❌ 不适合复杂文法(类爆炸问题)
❌ 性能不如编译执行
❌ 维护成本随文法复杂度增加
在实际开发中,应权衡需求复杂度,对于简单DSL可以使用解释器模式快速实现,对于复杂语言处理建议使用专业解析工具。
九、在Uniry中的应用
示例1:简单数学表达式解释器
using UnityEngine; using System.Collections.Generic; // 抽象表达式 public abstract class Expression { public abstract int Interpret(Dictionary<string, int> context); } // 终结符表达式 - 变量 public class VariableExpression : Expression { private string name; public VariableExpression(string name) { this.name = name; } public override int Interpret(Dictionary<string, int> context) { // 从上下文中获取变量值 if (context.ContainsKey(name)) { return context[name]; } throw new System.Exception($"变量 {name} 未定义"); } } // 终结符表达式 - 常量 public class ConstantExpression : Expression { private int value; public ConstantExpression(int value) { this.value = value; } public override int Interpret(Dictionary<string, int> context) { return value; } } // 非终结符表达式 - 加法 public class AddExpression : Expression { private Expression left; private Expression right; public AddExpression(Expression left, Expression right) { this.left = left; this.right = right; } public override int Interpret(Dictionary<string, int> context) { return left.Interpret(context) + right.Interpret(context); } } // 非终结符表达式 - 减法 public class SubtractExpression : Expression { private Expression left; private Expression right; public SubtractExpression(Expression left, Expression right) { this.left = left; this.right = right; } public override int Interpret(Dictionary<string, int> context) { return left.Interpret(context) - right.Interpret(context); } } // 非终结符表达式 - 乘法 public class MultiplyExpression : Expression { private Expression left; private Expression right; public MultiplyExpression(Expression left, Expression right) { this.left = left; this.right = right; } public override int Interpret(Dictionary<string, int> context) { return left.Interpret(context) * right.Interpret(context); } } // 表达式解析器 public class ExpressionParser { private Dictionary<string, int> variables = new Dictionary<string, int>(); // 解析表达式字符串 public Expression Parse(string expression) { // 这里简化处理,实际应用中需要更复杂的解析逻辑 if (expression.Contains("+")) { string[] parts = expression.Split('+'); return new AddExpression(Parse(parts[0]), Parse(parts[1])); } else if (expression.Contains("-")) { string[] parts = expression.Split('-'); return new SubtractExpression(Parse(parts[0]), Parse(parts[1])); } else if (expression.Contains("*")) { string[] parts = expression.Split('*'); return new MultiplyExpression(Parse(parts[0]), Parse(parts[1])); } else if (int.TryParse(expression, out int value)) { return new ConstantExpression(value); } else { return new VariableExpression(expression); } } // 设置变量值 public void SetVariable(string name, int value) { variables[name] = value; } // 获取当前变量表 public Dictionary<string, int> GetContext() { return variables; } } // 测试代码 public class MathInterpreterTest : MonoBehaviour { void Start() { ExpressionParser parser = new ExpressionParser(); // 设置变量 parser.SetVariable("x", 10); parser.SetVariable("y", 5); // 解析并计算表达式 TestExpression(parser, "x+y"); // 10 + 5 = 15 TestExpression(parser, "x-y"); // 10 - 5 = 5 TestExpression(parser, "x*y"); // 10 * 5 = 50 TestExpression(parser, "x+y*2"); // 10 + (5 * 2) = 20 } void TestExpression(ExpressionParser parser, string expression) { Expression exp = parser.Parse(expression); int result = exp.Interpret(parser.GetContext()); Debug.Log($"{expression} = {result}"); } }
示例2:简单AI行为脚本解释器
using UnityEngine; using System.Collections.Generic; // 抽象行为表达式 public abstract class AIActionExpression { public abstract void Interpret(AIContext context); } // 移动行为 public class MoveAction : AIActionExpression { private string direction; private float distance; public MoveAction(string direction, float distance) { this.direction = direction.ToLower(); this.distance = distance; } public override void Interpret(AIContext context) { Vector3 moveVector = Vector3.zero; switch (direction) { case "forward": moveVector = context.AITransform.forward * distance; break; case "back": moveVector = -context.AITransform.forward * distance; break; case "left": moveVector = -context.AITransform.right * distance; break; case "right": moveVector = context.AITransform.right * distance; break; case "up": moveVector = context.AITransform.up * distance; break; case "down": moveVector = -context.AITransform.up * distance; break; } context.AITransform.position += moveVector; Debug.Log($"AI移动: {direction} {distance}米"); } } // 等待行为 public class WaitAction : AIActionExpression { private float seconds; public WaitAction(float seconds) { this.seconds = seconds; } public override void Interpret(AIContext context) { Debug.Log($"AI等待: {seconds}秒"); // 实际游戏中可以使用协程实现等待 } } // 攻击行为 public class AttackAction : AIActionExpression { private string target; public AttackAction(string target) { this.target = target; } public override void Interpret(AIContext context) { Debug.Log($"AI攻击: {target}"); // 实际游戏中这里会实现攻击逻辑 } } // AI行为序列 public class ActionSequence : AIActionExpression { private List<AIActionExpression> actions = new List<AIActionExpression>(); public void AddAction(AIActionExpression action) { actions.Add(action); } public override void Interpret(AIContext context) { foreach (var action in actions) { action.Interpret(context); } } } // AI上下文 public class AIContext { public Transform AITransform { get; set; } public Dictionary<string, object> Variables { get; } = new Dictionary<string, object>(); } // AI脚本解析器 public class AIScriptParser { public AIActionExpression Parse(string script) { ActionSequence sequence = new ActionSequence(); // 分割脚本为多行 string[] lines = script.Split(new[] { '\n', ';' }, System.StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) { string trimmedLine = line.Trim(); if (string.IsNullOrEmpty(trimmedLine)) continue; // 分割命令和参数 string[] parts = trimmedLine.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 0) continue; string command = parts[0].ToLower(); switch (command) { case "move": if (parts.Length 编程客栈>= 3) { string direction = parts[1]; if (float.TryParse(parts[2], out float distance)) { sequence.AddAction(new MoveAction(direction, distance)); } } break; case "wait": if (parts.Length >= 2 && float.TryParse(parts[1], out float seconds)) { sequence.AddAction(new WaitAction(seconds)); } break; case "attack": if (parts.Length >= 2) { sequence.AddAction(new AttackAction(parts[1])); } break; } } return sequence; } } // AI控制器 public class AIController : MonoBehaviour { public string aiScript = @" move forward 5 wait 2 attack player move back 3 wait 1 "; private AIContext context; private AIActionExpression behavior; void Start() { context = new AIContext { AITransform = transform }; AIScriptParser parser = new AIScriptParser(); behavior = parser.Parse(aiScript); // 执行AI脚本 behavior.Interpret(context); } }
示例3:对话条件解释器
using UnityEngine; using System.Collections.Generic; // 抽象条件表达式 public abstract class ConditionExpression { public abstract bool Interpret(DialogueContext context); } // 变量条件 public class VariableCondition : ConditionExpression { private string variableName; private int expectedValue; private string comparison; // "==", ">", "<", etc. public VariableCondition(string variableName, string comparison, int expectedValue) { this.variableName = variableName; this.comparison = comparison; this.expectedValue = expectedValue; } public override bool Interpret(DialogueContext context) { if (!context.Variables.ContainsKey(variableName)) { Debug.LogWarning($"变量 {variableName} 未定义"); return false; } int actualValue = context.Variables[variableName]; switch (comparison) { case "==": return actualValue == expectedValue; case "!=": return actualValue != expectedValue; case ">": return actualValue > expectedValue; case "<": return actualValue < expectedValue; case ">=": return actualValue >= expectedValue; case "<=": return actualValue <= expectedValue; default: Debug.LogWarning($"未知比较运算符: {comparison}"); return false; } } } // 逻辑与条件 public class AndCondition : ConditionExpression { private ConditionExpression left; private ConditionExpression right; public AndCondition(ConditionExpression left, ConditionExpression right) { this.left = left; this.right = right; } public override bool Interpret(DialogueContext context) { return left.Interpret(context) && right.Interpret(context); } } // 逻辑或条件 public class OrCondition : ConditionExpression { private ConditionExpression left; private ConditionExpression right; public OrCondition(ConditionExpression left, ConditionExpression right) { this.left = left; this.right = right; } public override bool Interpret(DialogueContext context) { return left.Interpret(context) || right.Interpret(context); } } // 非条件 public class NotCondition : ConditionExpression { private ConditionExpression condition; public NotCondition(ConditionExpression condition) { this.condition = condition; } public override bool Interpret(DialogueContext context) { return !condition.Interpret(context); } } // 对话上下文 public class DialogueContext { public Dictionary<string, int> Variables { get; } = new Dictionary<string, int>(); } // 条件解析器 public class ConditionParser { public ConditionExpression Parse(string conditionStr) { // 这里简化处理,实际应用中需要更复杂的解析逻辑 if (conditionStr.Contains("&&")) { string[] parts = conditionStr.Split(new[] { "&&" }, System.StringSplitOptions.RemoveEmptyEntries); return new AndCondition(Parse(parts[0]), Parse(parts[1])); } else if (conditionStr.Contains("||")) { string[] parts = conditionStr.Split(new[] { "||" }, System.StringSplitOptions.RemoveEmptyEntries); return new OrCondition(Parse(parts[0]), Parse(parts[1])); } else if (conditionStr.StartsWith("!")) { return new NotCondition(Parse(conditionStr.Substring(1))); } else { // 解析变量条件 如: "health > 50" string[] parts = conditionStr.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 3) { string varName = parts[0]; string op = parts[1]; if (int.TryParse(parts[2], out int value)) { return new VariableCondition(varName, op, value); } } } throw new System.Exception($"无法解析条件: {conditionStr}"); } } // 对话选项 public class DialogueOption { public string Text { get; } public ConditionExpression Condition { get; } public DialogueOption(string text, ConditionExpression condition = null) { Text = text; Condition = condition; } public bool IsAvailable(DialogueContext context) { return Condition == null || Condition.Interpret(context); } } // 测试代码 public class DialogueConditionTest : MonoBehaviour { void Start() { DialogueContext context = new DialogueContext(); context.Variables["health"] = 75; context.Variables["hasKey"] = 1; context.Variables["karma"] = -10; ConditionParser parser = new ConditionParser(); TestCondition(parser, context, "health > 50"); // true TestCondition(parser, context, "hasKey == 1"); // true TestCondition(parser, context, "karma >= 0"); // false TestCondition(parser, context, "health > 50 && hasKey == 1"); // true TestCondition(parser, context, "health > 50 || karma >= 0"); // true TestCondition(parser, context, "!hasKey == 1"); // false // 创建对话选项 DialogueOption option1 = new DialogueOption("攻击敌人", parser.Parse("health > 50")); DialogueOption option2 = new DialogueOption("和平解决", parser.Parse("karma >= 0")); DialogueOption option3 = new DialogueOption("逃跑", null); // 无条件 Debug.Log($"选项1可用: {option1.IsAvailable(context)}"); // true Debug.Log($"选项2可用: {option2.IsAvailable(context)}"); // false Debug.Log($"选项3可用: {option3.IsAvailable(context)}"); // true } void TestCondition(ConditionParser parser, DialogueContext context, string conditionStr) { ConditionExpression condition = parser.Parse(conditionStr); bool result = condition.Interpret(context); Debug.Log($"{conditionStr} = {result}"); } }
在Unity中的实现建议
- 结合ScriptableObject:可以将表达式配置为ScriptableObject,便于在编辑器中设置
- 使用解析器生成器:对于复杂文法,考虑使用ANTLR等解析器生成器
- 限制文法复杂度:保持解释的语言简单,避免过度设计
- 缓存解析结果:对于频繁使用的表达式,可以缓存解析结果提高性能
- 与事件系统结合:将解释结果转换为游戏事件,降低耦合度
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论