可以对属性进行排序操作吗?有更好的方法吗?
Is ok to perform ordering operation on Property? Is there a better way of doing it?
我需要一个按分数排序的集合并维护其位置编号。我是这样做的:
private readonly ICollection<Score> _winningTable = new List<Score>();
public IEnumerable<Score> WinningTable
{
get
{
var orderedList = _winningTable.OrderByDescending(w => w.Score).ToList();
orderedList.ForEach(w =>
{
var index = orderedList.IndexOf(w);
// This will edit the object position and score
w.EditScore((decimal)w.Score, index + 1);
});
return orderedList.ToList();
}
}
在 属性 中,我正在维护订单并设置其位置 nbr。所以我只通过 属性 访问对象获胜分数。
我不确定这是否是正确的方法,我想知道是否有更好的方法。
不确定您使用 EditScore
方法的意图。您正在传递一个已经在实例内部的分数值。很高兴在您的问题中看到 class Score
的实现。
一般来说,我认为最好在方法和设置器中更新实例。我相信您的实例已通过某些代码更新了分数。
而且我怀疑你不需要index
,据我所知,它是直接从 Score 派生的。
无论如何,下面是您的代码的更简单版本。
public IEnumerable<Score> WinningTable
{
get
{
return _winningTable
.OrderByDescending(w => w.Score)
.Select((it, index) =>
{
it.EditScore((decimal)it.Score, index + 1);
return it;
})
.ToList();
}
}
如果你能摆脱方法EditScore
(这是更可取的):
public IEnumerable<Score> WinningTable
{
get
{
return _winningTable
.OrderByDescending(w => w.Score)
.ToList();
}
}
这是我对您的要求的看法:
- 您需要 WinningTable 属性 始终表示您的分数的有序列表。
- 您需要每个分数在列表中保持其位置索引。
根据第二个要求,我假设每个分数在任何给定时间只能在一个 WinningTable
内。因为否则它的位置索引会是什么?
我认为您的实施存在一些问题:
没有什么能阻止您将单个 Score 对象添加到多个 WinningTables
。在这种情况下,它的位置索引将不是您所期望的。
由于 惰性求值 IEnumerable
的性质,当我看到 IEnumerable
属性 时,我不要指望检索它是一项 costy 操作。但是,在您的 getter 中,您完全迭代了列表,这在您只需要检索 IEnumerable
时不会发生。换句话说,当我这样做时: var scores = obj.WinningTable;
我假设这将是一个 O(1) 操作,但在你的情况下它是 O(n^2) 因为你在 .ForEach
和如果你优化 .ForEach
,由于排序的原因,它将是 O(nlogn)。
在 getter 中执行所有逻辑是违反直觉的。在调用 属性 之前,分数对象不会有正确的位置索引。考虑这种情况:
var score = new Score(10);
obj.AddScore(score);
Console.WriteLine(score.Position); // ???
由于您依赖 属性 的 getter 来执行您的逻辑,因此代码将无法运行。
我的建议是为您的目的创建一个修改后的集合,并在那里应用您的逻辑。以下是该集合的示例实现(为了演示,使用 Score
class 的简化版本):
public class Score
{
public int Value { get; set; }
public int Position { get; set; } = -1;
}
public class SortedScoreCollection : Collection<Score>
{
protected override void ClearItems()
{
foreach (var item in this)
{
item.Position = -1;
}
base.ClearItems();
}
protected override void InsertItem(int index, Score item)
{
if (item.Position != -1)
{
throw new InvalidOperationException("Unable to add score. A score can only be inside a single collection.");
}
index = this.FindInsertionIndex(item);
item.Position = index + 1;
for (int i = index; i < this.Count; i++)
{
this[i].Position++;
}
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
this[index].Position = -1;
for (int i = index + 1; i < this.Count; i++)
{
this[i].Position--;
}
base.RemoveItem(index);
}
protected override void SetItem(int index, Score item)
{
var oldItem = this[index];
if (Equals(oldItem, item))
{
return;
}
if (item.Position != -1)
{
throw new InvalidOperationException("Unable to add score. A score can only be inside a single collection.");
}
this.RemoveItem(index);
this.Add(item);
}
private int FindInsertionIndex(Score item)
{
int index = 0;
while (index < this.Count && this[index].Value > item.Value)
{
index++;
}
return index;
}
}
// Usage:
private readonly ICollection<Score> _winningTable = new SortedScoreCollection();
public IEnumerable<Score> WinningTable => _winningTable;
这样上面解释的所有问题都将得到解决,无论您如何从集合中添加或删除它们,您的集合和所有乐谱对象将始终处于一致状态。请注意,在此实现中,删除和插入将是 O(n) 操作,检索 WinningTable
将是 O(1) 操作。
我需要一个按分数排序的集合并维护其位置编号。我是这样做的:
private readonly ICollection<Score> _winningTable = new List<Score>();
public IEnumerable<Score> WinningTable
{
get
{
var orderedList = _winningTable.OrderByDescending(w => w.Score).ToList();
orderedList.ForEach(w =>
{
var index = orderedList.IndexOf(w);
// This will edit the object position and score
w.EditScore((decimal)w.Score, index + 1);
});
return orderedList.ToList();
}
}
在 属性 中,我正在维护订单并设置其位置 nbr。所以我只通过 属性 访问对象获胜分数。 我不确定这是否是正确的方法,我想知道是否有更好的方法。
不确定您使用 EditScore
方法的意图。您正在传递一个已经在实例内部的分数值。很高兴在您的问题中看到 class Score
的实现。
一般来说,我认为最好在方法和设置器中更新实例。我相信您的实例已通过某些代码更新了分数。
而且我怀疑你不需要index
,据我所知,它是直接从 Score 派生的。
无论如何,下面是您的代码的更简单版本。
public IEnumerable<Score> WinningTable
{
get
{
return _winningTable
.OrderByDescending(w => w.Score)
.Select((it, index) =>
{
it.EditScore((decimal)it.Score, index + 1);
return it;
})
.ToList();
}
}
如果你能摆脱方法EditScore
(这是更可取的):
public IEnumerable<Score> WinningTable
{
get
{
return _winningTable
.OrderByDescending(w => w.Score)
.ToList();
}
}
这是我对您的要求的看法:
- 您需要 WinningTable 属性 始终表示您的分数的有序列表。
- 您需要每个分数在列表中保持其位置索引。
根据第二个要求,我假设每个分数在任何给定时间只能在一个 WinningTable
内。因为否则它的位置索引会是什么?
我认为您的实施存在一些问题:
没有什么能阻止您将单个 Score 对象添加到多个
WinningTables
。在这种情况下,它的位置索引将不是您所期望的。由于 惰性求值
IEnumerable
的性质,当我看到IEnumerable
属性 时,我不要指望检索它是一项 costy 操作。但是,在您的 getter 中,您完全迭代了列表,这在您只需要检索IEnumerable
时不会发生。换句话说,当我这样做时:var scores = obj.WinningTable;
我假设这将是一个 O(1) 操作,但在你的情况下它是 O(n^2) 因为你在.ForEach
和如果你优化.ForEach
,由于排序的原因,它将是 O(nlogn)。在 getter 中执行所有逻辑是违反直觉的。在调用 属性 之前,分数对象不会有正确的位置索引。考虑这种情况:
var score = new Score(10); obj.AddScore(score); Console.WriteLine(score.Position); // ???
由于您依赖 属性 的 getter 来执行您的逻辑,因此代码将无法运行。
我的建议是为您的目的创建一个修改后的集合,并在那里应用您的逻辑。以下是该集合的示例实现(为了演示,使用 Score
class 的简化版本):
public class Score
{
public int Value { get; set; }
public int Position { get; set; } = -1;
}
public class SortedScoreCollection : Collection<Score>
{
protected override void ClearItems()
{
foreach (var item in this)
{
item.Position = -1;
}
base.ClearItems();
}
protected override void InsertItem(int index, Score item)
{
if (item.Position != -1)
{
throw new InvalidOperationException("Unable to add score. A score can only be inside a single collection.");
}
index = this.FindInsertionIndex(item);
item.Position = index + 1;
for (int i = index; i < this.Count; i++)
{
this[i].Position++;
}
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
this[index].Position = -1;
for (int i = index + 1; i < this.Count; i++)
{
this[i].Position--;
}
base.RemoveItem(index);
}
protected override void SetItem(int index, Score item)
{
var oldItem = this[index];
if (Equals(oldItem, item))
{
return;
}
if (item.Position != -1)
{
throw new InvalidOperationException("Unable to add score. A score can only be inside a single collection.");
}
this.RemoveItem(index);
this.Add(item);
}
private int FindInsertionIndex(Score item)
{
int index = 0;
while (index < this.Count && this[index].Value > item.Value)
{
index++;
}
return index;
}
}
// Usage:
private readonly ICollection<Score> _winningTable = new SortedScoreCollection();
public IEnumerable<Score> WinningTable => _winningTable;
这样上面解释的所有问题都将得到解决,无论您如何从集合中添加或删除它们,您的集合和所有乐谱对象将始终处于一致状态。请注意,在此实现中,删除和插入将是 O(n) 操作,检索 WinningTable
将是 O(1) 操作。