我有一个要返回的对象列表,我需要检查它们是否按正确的顺序由其中一个属性(如枚举中定义)返回。
举一个更好的例子。假设我有一个 List(),其中包含以下内容
{ Name: "name1", Action: SomeAction.Read, Value: someValue },
{ Name: "name2", Action: SomeAction.Create, Value: someValue },
{ Name: "name3", Action: SomeAction.Update, Value: someValue },
{ Name: "name4", Action: SomeAction.Delete, Value: someValue },
{ Name: "name5", Action: SomeAction.Archive, Value: someValue }
枚举包含以下内容:
public enum SomeAction
{
Read,
Create,
Update,
Delete,
Download,
Archive,
Restore
}
有没有办法与枚举进行比较,以检查列表是否按 Action 属性正确排序?
需要注意的一点是,列表可能不包含具有枚举中定义的所有操作的属性的对象,因此列表可能只有读/写/删除属性等。
枚举可以分配一个整数值(有些人可能称之为"序数")。可以通过强制转换来获取此整数值。
int valueOfEnum = (int)SomeEnum.SomeEnumValue;
在您的情况下,您尚未分配整数值,这很好。如果不分配它们,则 C# 将自动为你分配它们。它们从0
开始,然后从那里递增。
下面是有关使用这些枚举"值"的 MSDN 文档。
可以按希望出现的顺序将项放入enum SomeAction
定义中,并让 C# 自动分配值。这可能是您需要做的全部工作。
您还可以为每个值分配值,以指定您喜欢的顺序。如果要以不同的顺序声明它们,这会有所帮助。
public enum SomeAction
{
Read = 1,
Create = 3,
Update = 2, // Update should come before Create in my list, so I give it a lower value
// ...
如果要按组对操作进行排序,可以使用此手动分配技术来分配重复值(可能是Archive = 1
和Update = 1
)。
现在,您已经有了比较列表中项目的基础,有几种方法可以确保它们按顺序排列:
只需排序即可
确保约束的一个好方法是自己完成工作。排序,它将被排序:)
仅当您的排序函数产生稳定的排序时,这才有效。MSDN 上的文档说OrderBy
进行稳定的排序,所以如果你使用这种方法就可以了。
仅当您有能力重新排序列表中的项目时,它也将起作用。从您的操作的定义(每个操作都依赖于先前状态的操作列表)来看,我不确定这是真的。您可能需要选择其他方法来检查订单。
var sortedList = yourList.OrderBy(item => (int)item.Action).ToList();
将列表与自身的排序版本进行比较
如果您正在执行错误检查,但不想更正错误,这将非常有用。
此方法不会更改列表,因此对于您正在查看的操作类型可能是安全的。
var sortedList = yourList.OrderBy(item => (int)item.Action);
bool isSorted = Enumerable.SequenceEqual(yourList, sortedList);
手动编写比较算法
如果您需要严格控制内存分配和 CPU 使用率,这可能很有用。
你可能不需要这种级别的控制,除非你的列表真的很大,或者你正处于高性能代码的紧密循环(如视频游戏绘制循环)的中间。即使在这些情况下,如果可能的话,您也可能需要考虑重构代码,以便此检查不会处于紧密循环的中间。
关于为什么我使用for
而不是foreach
,请参阅 - 在 .NET 中,哪个循环运行得更快,"for"还是"foreach"?
// Note that you have to build a custom type for your list items to keep high performance,
// or write this inline instead of as a function, to avoid the perf hit of IList<dynamic>
bool IsMySpecificListTypeSorted(List<MyCustomListItemType> theList) {
int previousOrdinal = -1;
// Not using foreach because it is "8x slower" in tests
// and you're micro-optimizing in this scenario
for(int index = 0; index < theList.Count; ++index) {
var item = theList[index];
var currentOrdinal = (int)item.Action;
if(currentOrdinal < previousOrdinal) {
return false;
}
previousOrdinal = currentOrdinal;
}
return true;
}
如果我理解正确,您想按枚举中的项目顺序排序,对吗?
如果确实如此,那么您可能知道枚举项具有 int 值。 考虑到这一点,您可以像这样订购:
List<dynamic> demo = new List<dynamic>();
demo.Add(new { Name = "name1", Action = SomeAction.Read, Value = "someValue" });
demo.Add(new { Name = "name1", Action = SomeAction.Restore, Value = "someValue" });
demo.Add(new { Name = "name1", Action = SomeAction.Update, Value = "someValue" });
demo = demo.OrderBy(e => (int)e.Action).ToList();
编辑我同意这很可能不是正确的方法。基于枚举排序。OP 可能希望完全改变方法。
我会这样做,使用 linq 扩展Zip
var inorder= Enum.GetNames(typeof(SomeAction))
.Zip(array,(x,y) => y.Contains(x)) // change this condition to get uniqueness.
.All(x=>x);
检查此Demo
如果您有正确排序SomeAction
的项目,那么只需排序和比较:
List<MyType> list = ...
var orderedRight = list
.OrderBy(item => (int) (item.Action))
.Select(item => item.Action);
boolean inCorrectOrder = list.SequenceEqual(orderedRight
.Select(item => item.Action));
如果要使用任意顺序,请添加映射:
Dictionary<SomeAction, int> map = new Dictionary<SomeAction, int>() {
{SomeAction.Read, 2},
{SomeAction.Create, 1}, // Create should be 1st
{SomeAction.Update, 2}, // Read and Update considered interchangeable
...
{SomeAction.Create, 15},
};
...
var orderedRight = list
.OrderBy(item => map[item.Action])
.Select(item => item.Action);
boolean inCorrectOrder = list.SequenceEqual(orderedRight
.Select(item => item.Action));
您可以为枚举分配特定值,然后实现 IComparable,如下所示
public class TestObj : IComparable<TestObj>
{
public string Name { get; set; }
public SomeAction Action { get; set; }
public string Value { get; set; }
public TestObj(string name, SomeAction action, string value)
{
Name = name;
Action = action;
Value = value;
}
public enum SomeAction
{
Read = 0,
Create = 1,
Update = 2,
Delete = 3,
Download = 4,
Archive = 5,
Restore = 6
}
public override string ToString()
{
return string.Format("Name: {0}, Action: {1}, Value: {2}", Name, Action.ToString(), Value);
}
public int CompareTo(TestObj obj)
{
return this.Action.CompareTo(obj.Action);
}
}
按如下方式创建列表并按正确顺序排序、输出
List<TestObj> objs = new List<TestObj>();
objs.Add(new TestObj("1", Form1.SomeAction.Delete));
objs.Add(new TestObj("2", Form1.SomeAction.Archive));
objs.Add(new TestObj("3", Form1.SomeAction.Read));
objs.Add(new TestObj("4", Form1.SomeAction.Update));
objs.Add(new TestObj("5", Form1.SomeAction.Create));
objs.Sort();
foreach (var item in objs)
{
Console.WriteLine(item.ToString());
}
假设"items"是项目的数组,请使用以下方法:
Enumerable.Range(1, items.Length - 1)
.All(i => (int)(items[i - 1].Action) < (int)(items[i].Action));
您可以使用简单的 for 循环来检查是否满足条件。
var enums = Enum.GetValues(typeof(SomeAction)).Cast<SomeAction>();
for (int i = 0; i < list.Count; i++)
{
var listAction = list.ElementAt(i).Action;
var indexEnumAction = (SomeAction)i;
Console.WriteLine("{0} == {1} ? {2}", listAction, indexEnumAction, listAction == indexEnumAction);
}
输出:
Read == Read ? True
Create == Create ? True
Update == Update ? True
Delete == Delete ? True
Archive == Download ? False
最快的方法:
var enums = Enum.GetValues(typeof(SomeAction)).Cast<SomeAction>();
for (int i = 0; i < list.Count; i++)
{
if (list.ElementAt(i).Action != (SomeAction)i)
{
return false;
}
}
return true;