Вам нужен набор. Это гарантирует, что элементы, введенные в него, являются уникальными (на основе некоторого квалификатора, который вы настроите). Вот как это делается:
Сначала измените вашу структуру на класс. Это действительно невозможно обойти.
Во-вторых, предоставьте реализацию IEqualityComparer<stAppInfo>
. Это может быть хлопотно, но именно это заставляет ваш набор работать (что мы увидим через минуту):
public class AppInfoComparer : IEqualityComparer<stAppInfo>
{
public bool Equals(stAppInfo x, stAppInfo y) {
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return Equals(x.sTitle, y.sTitle) && Equals(x.sRelativePath,
y.sRelativePath);
}
// this part is a pain, but this one is already written
// specifically for your question.
public int GetHashCode(stAppInfo obj) {
unchecked {
return ((obj.sTitle != null
? obj.sTitle.GetHashCode() : 0) * 397)
^ (obj.sRelativePath != null
? obj.sRelativePath.GetHashCode() : 0);
}
}
}
Затем, когда придет время сделать свой сет, сделайте следующее:
var appInfoSet = new HashSet<stAppInfo>(new AppInfoComparer());
foreach (ListViewItem item in listView1.Items)
{
var newItem = new stAppInfo {
sTitle = item.Text,
sRelativePath = item.SubItems[1].Text,
sCmdLine = item.SubItems[2].Text,
bFindInstalled = (item.SubItems[3].Text.Equals("Sí")) ? true : false,
sFindTitle = item.SubItems[4].Text,
sFindVersion = item.SubItems[5].Text,
bChecked = (item.SubItems[6].Text.Equals("Sí")) ? true : false};
appInfoSet.Add(newItem);
}
appInfoSet
теперь содержит коллекцию stAppInfo
объектов с уникальными комбинациями Заголовок / Путь, согласно вашему требованию. Если вам нужен массив, сделайте это:
stAppInfo[] appInfo = appInfoSet.ToArray();
Примечание: я выбрал эту реализацию, потому что она выглядит так, как вы уже делаете вещи. Он легко читается для цикла (хотя мне не нужна переменная counter). Это не связано с LINQ (что может быть хлопотно, если вы не знакомы с ним). Он не требует никаких внешних библиотек за пределами того, что предоставляет .NET Framework. И, наконец, он предоставляет массив, как вы и просили. Что касается чтения файла из INI-файла, надеюсь, вы видите, что единственное, что изменится, это ваш foreach
цикл.
Обновление
Хэш-коды могут быть болью. Возможно, вы задавались вопросом, зачем вам вообще их вычислять. В конце концов, не могли бы вы просто сравнить значения заголовка и относительного пути после каждой вставки? Конечно, конечно, вы могли бы, и именно так работает другой набор, называемый SortedSet
. SortedSet
заставляет вас реализовать IComparer
так же, как я реализовал IEqualityComparer
выше.
Итак, в этом случае AppInfoComparer
будет выглядеть так:
private class AppInfoComparer : IComparer<stAppInfo>
{
// return -1 if x < y, 1 if x > y, or 0 if they are equal
public int Compare(stAppInfo x, stAppInfo y)
{
var comparison = x.sTitle.CompareTo(y.sTitle);
if (comparison != 0) return comparison;
return x.sRelativePath.CompareTo(y.sRelativePath);
}
}
И тогда единственное другое изменение, которое вам нужно сделать, это использовать SortedSet вместо HashSet:
var appInfoSet = new SortedSet<stAppInfo>(new AppInfoComparer());
На самом деле так намного проще, что вам, наверное, интересно, что дает? Причина, по которой большинство людей выбирают HashSet
вместо SortedSet
, заключается в производительности. Но вы должны сбалансировать это с тем, насколько вы на самом деле заботитесь, так как вы будете поддерживать этот код. Я лично использую инструмент под названием Resharper, который доступен для Visual Studio, и он вычисляет эти хэш-функции для меня, потому что я думаю, что вычислять их тоже очень сложно.
(Я буду говорить о сложности двух подходов, но если вы уже знаете это или не заинтересованы, не стесняйтесь пропустить его.)
SortedSet
имеет сложность O (log n), то есть каждый раз, когда вы вводите новый элемент, вы фактически пройдете половину своего набора и сравните. Если он не найдет вашу запись, он перейдет на полпути между последним предположением и группой слева или справа от этого предположения, быстро сократив места для вашего элемента, чтобы скрыть. Для миллиона записей требуется около 20 попыток. Совсем неплохо. Но если вы выбрали хорошую функцию хеширования, то HashSet
может выполнять ту же работу, в среднем, в одном сравнении, а именно O (1). И прежде чем вы подумаете, что 20 - это не на самом деле такое большое дело по сравнению с 1 (после того, как все компьютеры работают довольно быстро), помните, что вам пришлось вставить эти миллионы элементов, поэтому в то время как HashSet
потребовалось около миллиона попыток для создания этого набора SortedSet
потребовалось несколько миллионов попыток. Но есть цена - HashSet
ломается (очень плохо), если вы выбираете плохую функцию хеширования. Если числа для множества предметов уникальны, то они будут сталкиваться в HashSet
, который затем придется пробовать снова и снова. Если множество предметов сталкиваются с одним и тем же номером, они будут повторять шаги друг друга, и вы будете ждать долго. Миллионная запись займет миллион раз миллион попыток - HashSet
перешло в O (n ^ 2). Что важно для этих обозначений big-O (а именно O (1), O (log n) и O (n ^ 2) на самом деле), так это то, насколько быстро число в скобках увеличивается с увеличением n. Медленный рост или отсутствие роста лучше. Быстрый рост иногда неизбежен. Для дюжины или даже сотен предметов разница может быть незначительной - но если вы можете привыкнуть программировать эффективные функции так же легко, как альтернативы, тогда стоит подготовить себя к этому, так как проблемы являются самыми дешевыми, чтобы исправить их ближе всего к Точка, где вы создали эту проблему.