C# 如何在插入列表时强制执行基于规则的对象排序
我有一个接口C# 如何在插入列表时强制执行基于规则的对象排序,c#,operator-precedence,C#,Operator Precedence,我有一个接口IRenderable,还有一个类,用于管理实现该接口的类的所有实例的呈现 我的代码中有很多类,希望其他人创建实现相同接口的类 由于渲染的性质,我想说“在这个类的实例之前绘制这个类的实例” 典型的方法是让每个类实现一个DrawOrder属性,但是我不喜欢这样,因为类没有明确的DrawOrder值,重要的是相对顺序。如果我给每个类一个DrawOrder属性,那么任何实现接口的人都需要知道所有类的值是什么。显然,如果许多人能够实现自己的类,这是不可能的 我希望能够定义“ClassA在C
IRenderable
,还有一个类,用于管理实现该接口的类的所有实例的呈现
我的代码中有很多类,希望其他人创建实现相同接口的类
由于渲染的性质,我想说“在这个类的实例之前绘制这个类的实例”
典型的方法是让每个类实现一个DrawOrder
属性,但是我不喜欢这样,因为类没有明确的DrawOrder值,重要的是相对顺序。如果我给每个类一个DrawOrder
属性,那么任何实现接口的人都需要知道所有类的值是什么。显然,如果许多人能够实现自己的类,这是不可能的
我希望能够定义“ClassA在ClassB之前,ClassC在ClassA之前”的规则,然后在制定绘图顺序/添加实例时,我可以推断出正确的绘图顺序。其他实现接口的人可以添加他们自己的与内置实现相关的规则以及他们自己的添加
编辑:我希望有一个类可以管理规则和维护订单,如下所示:
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();
}
类渲染器
{
私人名单规则;
私有可渲染列表;
//添加到规则列表中
void EnforceBefore(第一类,第二类);
//插入确保遵守所有规则的项目。
空心插入件(i可测量的分类指示器);
void RenderAll();
}
然后类可以适当地添加规则(或者我可以有一个接口方法来返回它们)
下面是一个不起作用的快速测试
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;
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
班级计划
{
静态void Main(字符串[]参数)
{
List MyList=新列表{“where”、“second”、“first”、“third”、“first”、“third”、“second”};
RuleBasedComparer RuleComparer=新的RuleBasedComparer();
//我希望确保“first”的所有实例都出现在“second”的所有实例之前的列表中
//并且“second”的所有实例都出现在列表中“third”的所有实例之前。
//我不在乎“where”出现在哪里(或任何超出上述规则的内容)
添加规则(“第一”、“第二”);
添加规则(“第二”、“第三”);
MyList.Sort(规则比较器);
foreach(MyList中的var项)
控制台写入线(项目);
Console.ReadKey();
}
}
公共类基于规则的比较程序:比较程序
{
私有类OrderRule
{
以前只公开阅读;
公开阅读后;
公共秩序规则(T之前,T之后)
{
之前=之前;
之后=之后;
}
}
私有列表_Rules=新列表();
公共无效添加规则(T之前,T之后)
{
_添加(新的OrderRule(之前、之后));
}
公共覆盖整型比较(TX,TY)
{
//查找此对的适用规则(如果有)
var applicatablerule=_Rules.Where(or=>or.After.Equals(x)&&or.Before.Equals(y)||
或.After.Equals(y)和&or.Before.Equals(x)).SingleOrDefault();
if(applicatablerule!=null)
{
//如果有规则,那么如果x应该在y之前,那么返回-1,否则返回1
if(适用规则Before等于(x))
返回-1;
其他的
返回1;
}
其他的
{
//如果没有规则存在,那么就说它们是平等的
返回0;
}
}
}
TL;DR:我如何从“类a在类B之前”的规则转变为实例/类的明确顺序。
由于缺乏完整的规则而产生的歧义不重要,我只希望遵守现有的规则。您可以添加两个属性,以类型作为参数,并要求目标类是可
IRenderable
[Before(typeof(MyClass))]
[After(typeof(MyClass))]
然后,您必须获取实现IRenderable
的所有类并检索这些属性
然后,你所要做的就是按照正确的顺序对它们进行排序(我依靠一个算法专家在这个问题上想出一个奇特的算法名称)
如果结果不明确(即:两个类位于同一个类之后),您必须决定要做什么
当然,您可以通过相同的接口实现类似的逻辑,但在我看来,这稍微超出了IRenderable
的职责范围,因此我会制作另一个
嗯,
Bab.渲染器能否确定所有类的顺序,即使是那些尚未实现的类?如果是这样,那么您可以创建一个
i比较程序
。如果您不知道将来实现的类的期望顺序是什么,那么这并不比依赖实现者显式指定绘制顺序更可行
通常,对于渲染,通常可以根据对象的属性确定绘制顺序,因此可以使用比较器根据已知的渲染属性(如透明度、ui层等)确定渲染顺序
public class RenderOrderComparer : IComparer<IRenderable>
{
public int Compare(IRenderable a, IRenderable b)
{
if (a.IsOverlay && !b.IsOverlay)
return -1;
if (b.IsOverlay && !a.IsOverlay)
return 1,
if (a.IsTransparent && !b.IsTransparent)
return -1;
if (b.IsTransparent && !a.IsTransparent)
return 1;
// ...and so on.
return 0;
}
}
公共类RenderComparer:IComparer
{
公共整数比较(IRenderable a、IRenderable b)
{
if(a.IsOverlay&!b.IsOverlay)
返回-1;
if(b.IsOverlay&!a.IsOverlay)
返回1,
if(a.IsTransparent&&!b.IsTransparent)
返回-1;
if(b.IsTransparent&!a.IsTransparent)
返回1;
//……等等。
返回0;
}
}
(注意,应该考虑扩展比较器,而不是实现ICOMPARER,请参阅)
另一个选项是更基于规则的每类解决方案,您可以通过实现public interface IRenderable : IComparable<IRenderable>
{
int Id { get; }
}
public class SomeRenderable : IRenderable
{
public int CompareTo(IRenderable other)
{
if (other is SomeRenderable)
return 0;
if (other is OtherRenderableType)
return 1;
}
}
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);
}
}
}