插入列表时如何执行基于规则的对象排序 [英] How to enforce rule-based object ordering when inserting into a list
问题描述
我有一个接口IRenderable
和一个用于管理呈现实现该接口的类的所有实例的类.
I have an interface IRenderable
and a class that manages rendering all instances of classes implementing the interface.
我的代码中有很多类,并且希望其他人创建实现相同接口的类.
I have many classes in my code and will expect others to create classes that implement the same interface.
由于渲染的性质,我想说些类似的事情,例如在此类的实例之前绘制此类的实例".
Due to the nature of rendering I want to say things like "Draw instances of this class before instances of this class".
典型的方法是让每个类都实现一个DrawOrder
属性,但是我不喜欢这样做,因为类没有明确的绘制顺序值,重要的是相对顺序.如果我给每个类一个DrawOrder
属性,则实现该接口的任何人都需要知道所有类的值是什么.显然,如果许多人可以实现自己的类,这是不可能的.
The typical approach is to have every class implement a DrawOrder
property, however I dislike this because classes don't have a definite draw order value, it's the relative order that matters. If I gave every class a DrawOrder
property anyone implementing the interface would need to know what the values of all classes were. Obviously this isn't possible if many people could implement their own class.
我想要的是能够定义这样的规则:"ClassA在ClassB之前,ClassC在ClassA之前",然后在制定绘制顺序/添加实例时,我可以推断出正确的绘制顺序.其他实现该接口的人可以添加自己的与内置实现有关的规则以及自己的添加项.
What I'd like is to be able to define rules that say "ClassA before ClassB, ClassC before ClassA", then when working out the draw order / adding instances I could infer a correct drawing order. Others implementing the interface could add their own rules relating to built in implementations and their own additions.
编辑:我希望找到一些类来管理规则并维护订单,如下所示:
What I'm hoping for is some class that manages the rules and manages maintaining an order, something like below:
class Renderer
{
private List<Rule> Rules;
private List<IRenderable> Renderables;
// Adds to list of rules
void EnforceBefore(Type FirstClass, Type SecondClass);
// Inserts items ensuring all rules are followed.
void Insert(IRenderable ClassInstance);
void RenderAll();
}
然后类可以适当地添加规则(或者我可以有一个返回它们的接口方法).
Classes could then add rules as appropriate (or I could have an interface method that returns them).
以下是无效的快速测试
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
List<string> MyList = new List<string> { "wherever", "second", "first", "third", "first", "third", "second" };
RuleBasedComparer<string> RuleComparer = new RuleBasedComparer<string>();
// I want to ensure all instances of "first" appear in the list before all instances of "second"
// and all instances of "second" appear in the list before all instances of "third".
// I don't care where "wherever" appears (or anything beyond the above rules)
RuleComparer.AddRule("first", "second");
RuleComparer.AddRule("second", "third");
MyList.Sort(RuleComparer);
foreach (var item in MyList)
Console.WriteLine(item);
Console.ReadKey();
}
}
public class RuleBasedComparer<T> : Comparer<T>
{
private class OrderRule
{
public readonly T Before;
public readonly T After;
public OrderRule(T before, T after)
{
Before = before;
After = after;
}
}
private List<OrderRule> _Rules = new List<OrderRule>();
public void AddRule(T before, T after)
{
_Rules.Add(new OrderRule(before, after));
}
public override int Compare(T x, T y)
{
// Find the applicable rule for this pair (if any)
var ApplicableRule = _Rules.Where(or => or.After.Equals(x) && or.Before.Equals(y) ||
or.After.Equals(y) && or.Before.Equals(x)).SingleOrDefault();
if (ApplicableRule != null)
{
// If there is a rule then if x should be before y then return -1, otherwise return 1
if (ApplicableRule.Before.Equals(x))
return -1;
else
return 1;
}
else
{
// If no rule exists then say they are equal
return 0;
}
}
}
TL; DR:如何从诸如"ClassB之前的ClassA"之类的规则转换为实例/类的确定顺序.
缺乏完整规则而造成的歧义无关紧要,我只希望遵守现有规则.
TL;DR: How do I go from rules that say things like "ClassA before ClassB" to a definite ordering of instances/classes.
Ambiguities created by a lack of complete rules shouldn't matter, I just want the existing rules adhered to.
推荐答案
我设法找出了一种似乎可行的方法(下面的完整代码示例).
I've managed to figure out a method that seems to work (complete code example below).
基本上,我添加了规则,然后将规则中包含的所有内容按可能的顺序分配,方法是将它们一个接一个地插入到列表中,并在添加每个规则时遵守这些规则.该项目在列表中的位置成为排序.比较列表中的2项内容(不是规则)时,我只返回0.
Basically I add my rules, then I assign everything included in a rule with a possible ordering by inserting them into a list one by one, observing the rules as I add each one. The position of the item in the list becomes the ordering. When comparing 2 things in the list (not in a rule) I just return 0.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
List<string> MyList = new List<string> { "second", "first", "second2", "wherever", "third", "second2", "third", "second", "first" };
RuleBasedComparer<string> RuleComparer = new RuleBasedComparer<string>();
// I want to ensure all instances of "first" appear in the list before all instances of "second" and "second2"
// and all instances of "second" and "second2" appear in the list before all instances of "third".
// I don't care where "wherever" appears (or anything beyond the above rules)
RuleComparer.AddRule("first", "second");
RuleComparer.AddRule("first", "second2");
RuleComparer.AddRule("second", "third");
RuleComparer.AddRule("second2", "third");
MyList.Sort(RuleComparer);
foreach (var item in MyList)
Console.WriteLine(item);
Console.ReadKey();
}
}
public class RuleBasedComparer<T> : Comparer<T>
{
private class OrderRule
{
public readonly T Before;
public readonly T After;
public OrderRule(T before, T after)
{
Before = before;
After = after;
}
}
private List<OrderRule> _Rules = new List<OrderRule>();
private List<T> DesiredOrdering = new List<T>();
private bool _NeedToCalculateOrdering = true;
public void AddRule(T before, T after)
{
if (!_NeedToCalculateOrdering)
throw new InvalidOperationException("Cannot add rules once this comparer has.");
_Rules.Add(new OrderRule(before, after));
}
private void CalculateOrdering()
{
_NeedToCalculateOrdering = false;
var ItemsToOrder = _Rules.SelectMany(r => new[] { r.Before, r.After }).Distinct();
foreach (var ItemToOrder in ItemsToOrder)
{
var MinIndex = 0;
var MaxIndex = DesiredOrdering.Count;
foreach (var Rule in _Rules.Where(r => r.Before.Equals(ItemToOrder)))
{
var indexofAfter = DesiredOrdering.IndexOf(Rule.After);
if (indexofAfter != -1)
{
MaxIndex = Math.Min(MaxIndex, indexofAfter);
}
}
foreach (var Rule in _Rules.Where(r => r.After.Equals(ItemToOrder)))
{
var indexofBefore = DesiredOrdering.IndexOf(Rule.Before);
if (indexofBefore != -1)
{
MinIndex = Math.Max(MinIndex, indexofBefore + 1);
}
}
if (MinIndex > MaxIndex)
throw new InvalidOperationException("Invalid combination of rules found!");
DesiredOrdering.Insert(MinIndex, ItemToOrder);
}
}
public override int Compare(T x, T y)
{
if (_NeedToCalculateOrdering)
CalculateOrdering();
if (x == null && y != null)
{
return -1;
}
else if (x != null && y == null)
return 1;
else if (x == null && y == null)
return 0;
// Find the applicable rule for this pair (if any)
var IndexOfX = DesiredOrdering.IndexOf(x);
var IndexOfY = DesiredOrdering.IndexOf(y);
if (IndexOfX != -1 && IndexOfY != -1)
{
// We have a definite order
if (IndexOfX > IndexOfY)
return 1;
else if (IndexOfX < IndexOfY)
return -1;
else
return 0;
}
else if (IndexOfX != -1)
{
return -1;
}
else if (IndexOfY != -1)
{
return 1;
}
else
{
return 0; // Or maybe compare x to y directly
//return Comparer<T>.Default.Compare(x, y);
}
}
}
这篇关于插入列表时如何执行基于规则的对象排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!