如何将C#游戏中的LINQ查询逻辑转换为数据驱动规则模式?
Hey there! It sounds like you're already crushing it with the command pattern for data-driven actions—extending that approach to your LINQ queries is a smart move. Let's break down how to model those query operations as reusable, data-driven rules that you can tweak without recompiling.
Core Idea: Model Each LINQ Operation as a Rule
Just like your Command interface, we'll define a common interface for all query rules. Each rule represents one step in your LINQ chain (a Select, Where, or custom transformation), and we'll chain them together dynamically using the rules list.
Step 1: Define the Rule Interface
First, create an interface that every rule will implement. Since LINQ operations transform sequences of data, our rule's method will take an IEnumerable<float> and return a new transformed sequence:
public interface IQueryRule { IEnumerable<float> Apply(IEnumerable<float> input); }
Step 2: Implement Specific Rules
Now, turn each part of your original LINQ query into a concrete rule class. Each rule will encapsulate one operation, with configurable parameters so you can reuse them:
// Handles x => x + value public class AddRule : IQueryRule { private readonly float _valueToAdd; public AddRule(float valueToAdd) { _valueToAdd = valueToAdd; } public IEnumerable<float> Apply(IEnumerable<float> input) { return input.Select(x => x + _valueToAdd); } } // Handles Where(x => x < threshold) public class LessThanFilterRule : IQueryRule { private readonly float _threshold; public LessThanFilterRule(float threshold) { _threshold = threshold; } public IEnumerable<float> Apply(IEnumerable<float> input) { return input.Where(x => x < _threshold); } } // Handles x => x * multiplier public class MultiplyRule : IQueryRule { private readonly float _multiplier; public MultiplyRule(float multiplier) { _multiplier = multiplier; } public IEnumerable<float> Apply(IEnumerable<float> input) { return input.Select(x => x * _multiplier); } } // Handles x > max ? max : x public class ClampUpperRule : IQueryRule { private readonly float _maxValue; public ClampUpperRule(float maxValue) { _maxValue = maxValue; } public IEnumerable<float> Apply(IEnumerable<float> input) { return input.Select(x => x > _maxValue ? _maxValue : x); } }
Step 3: Assemble Your Rules & Run the Query
Now, you can build your rules list to match the original LINQ query. For your example, the rules would look like this:
private float[] data; public List<IQueryRule> rules; // Initialize rules to match your original ExampleQuery public void InitializeRules() { rules = new List<IQueryRule> { new AddRule(2), new LessThanFilterRule(50), new MultiplyRule(3), new MultiplyRule(10), new ClampUpperRule(999) }; }
For the Query method, you don't need a manual foreach—use LINQ's Aggregate method to chain the rules together automatically. It takes your initial data sequence and applies each rule in order, just like the original LINQ chain:
public IEnumerable<float> Query() { // Start with the data, then apply each rule in sequence return rules.Aggregate(data.AsEnumerable(), (currentSequence, rule) => rule.Apply(currentSequence)); }
Bonus: Make Rules Configurable/Serializable
If you want to load rules from external files (like JSON or XML) without recompiling, you can:
- Add
[Serializable]attributes to your rule classes - Use a factory pattern to instantiate rules from configuration data. For example, a JSON config might look like:
[ {"Type": "AddRule", "Parameters": {"ValueToAdd": 2}}, {"Type": "LessThanFilterRule", "Parameters": {"Threshold": 50}}, {"Type": "MultiplyRule", "Parameters": {"Multiplier": 3}}, {"Type": "MultiplyRule", "Parameters": {"Multiplier": 10}}, {"Type": "ClampUpperRule", "Parameters": {"MaxValue": 999}} ] - Use reflection or a dependency injection container to create rule instances from this config.
Key Benefits
- No recompiles for new rules: Just add a new
IQueryRuleimplementation and update your rules list (or config file) - Reusable rules: You can mix and match rules across different queries
- Clear separation of concerns: Logic lives in rule classes, not monolithic query methods
内容的提问来源于stack exchange,提问作者Kevin Tanudjaja

