Как выполнить FindAll() для IList‹T›? (например, SortedList.Values)

Я работаю над проблемой в С# 2.0/.NET 2.0, где у меня есть Sortedlist и я хочу найти все "значения" (а не "ключи") этого SortedList для определенной подстроки и подсчитать, сколько вхождений .

Вот что я пытаюсь сделать:

{
   Sortedlist<string,string> mySortedList;
   // some code that instantiates mySortedList and populates it with data
   List<string> myValues = mySortedList.Values;  // <== does not work
   int namesFound = myValues.FindAll(ByName(someName)).Count;
}

Естественно, это не работает, поскольку mySortedList.Values ​​возвращает IList, а myValues ​​— это список. Я попытался «привести» IList, чтобы он был принят myValues, но, похоже, это не сработало.

Конечно, я могу перебрать mySortedList.Values ​​в цикле foreach, но на самом деле я не хочу этого делать.

У кого-нибудь есть предложения?

РЕДАКТИРОВАТЬ-1: Хорошо, похоже, что нет собственного способа сделать это легко. Я предполагал, что просто что-то упускаю, но, видимо, это не так. Так что я думаю, я просто собираюсь сделать "foreach" поверх IList.

Спасибо всем за отзывы! Я проголосовал за всех 1, потому что я думал, что все отзывы были хорошими. Спасибо еще раз! :-)

РЕДАКТИРОВАТЬ-2: Похоже, у CMS есть ответ, который я искал. Единственное предостережение (как указал Qwertie) заключается в том, что существует потенциальное снижение производительности, поскольку оно включает копирование всех значений в другой список и последующий поиск в этом списке от начала до конца. Так что для коротких списков этот ответ эффективен. Более длинные списки? ну это вам решать...


person Pretzel    schedule 06.03.2009    source источник


Ответы (5)


Поскольку интерфейс IList реализует IEnumerable, на самом деле вы можете получить List<T> значений, используя List<T> (IEnumerable) Constructor:

List<string> myValues = new List<string>(mySortedList.Values);
person Christian C. Salvadó    schedule 06.03.2009
comment
Ах, спасибо! Я проглядел что-то. Я не понял, что IList реализует IEnumerable. Если подумать об этом сейчас, это имеет смысл, но тогда мне это даже не пришло в голову. Это ответ! Спасибо КМС! :-) - person Pretzel; 06.03.2009
comment
При использовании этого решения следует учитывать влияние копирования всего списка на производительность. - person Qwertie; 06.03.2009
comment
Мне кажется, что возможность того, что это проблема производительности, не стоит небольшого рефакторинга для другого метода. Создавать новый потенциально большой объект только для того, чтобы вызвать для него простой метод? Какой-то там запах... - person codekaizen; 06.03.2009
comment
Qwertie: Хорошая мысль. Я действительно осознавал последствия копирования всего списка, но типичный сценарий имеет только от 10 до 50 значений. (в худшем случае ~400 значений) -- Относительно тривиальный объем работы во время выполнения, и мой код остается простым и читабельным. Справедливый обмен, я думаю. Спасибо еще раз! - person Pretzel; 07.03.2009

Вы не можете привести свойство Values ​​к List‹string›, потому что это не List‹string› — это Dictionary‹TKey, TValue›.ValueCollection.

Но если вы используете LinqBridge (для .NET Framework 2.0 с C# 3.0), эта проблема легко решается с помощью LINQ следующим образом:

SortedList<string, string> m = ...;
int namesFound = m.Values.Where(v => v.Contains("substring")).Count();

(если вы все еще используете C# 2.0, вы можете использовать LINQ бедняка вместо этого немного больше работы)

person Qwertie    schedule 06.03.2009

Жаль, что вы в .Net 2.0. Это именно то, для чего предназначен LINQ. ;)

Вы не можете этого сделать, так как FindAll() является членом List. Вы можете создать новый список в mySortedList.Values, но это кажется пустой тратой времени, поскольку новый объект и базовый массив должны быть выделены только для вызова функции.

Я бы просто написал служебную функцию в некотором классе для списков с именем FindAll(), а затем передал бы ваш IList и делегировал.

person codekaizen    schedule 06.03.2009


    static void Main(string[] args)
    {
        string someName = "two";
        SortedList<string, string> mySortedList = new SortedList<string,string>()
        {
            {"key1", "This is key one"},
            {"key2", "This is key two"},
            {"key3", "This is key three"},
        };

        int namesFound = mySortedList.Values.Where(i => i.Contains(someName)).Count();
        Console.WriteLine(namesFound);
        Console.ReadKey();
    }

В Framework 2.0, возможно, можно сделать это:

    static void Main(string[] args)
    {
        string someName = "two";
        SortedList<string, string> mySortedList = new SortedList<string,string>()
        {
            {"key1", "This is key one"},
            {"key2", "This is key two"},
            {"key3", "This is key three"},
        };

        int namesFound = FindAll(mySortedList.Values, someName).Count ;
        Console.WriteLine(namesFound);
        Console.ReadKey();
    }
    public static IList<String> FindAll(IList<String> items, string item)
    {
        List<String> result = new List<string>();
        foreach (String s in items)
        {
            if (s.Contains(item))
            {
                result.Add(s);
            }
        }
        return result;
    }

Но это то, чего вы действительно не хотели делать.

person Bill    schedule 06.03.2009
comment
Плакат сказал, что они используют .Net 2.0 - person Mitch Wheat; 06.03.2009
comment
Упс. пропустил тот. Извините, постер. - person Bill; 06.03.2009
comment
Я думаю, вы имели в виду общедоступную статическую строку списка (вместо того, чтобы возвращать другую строку IList), но я знал, что вы имели в виду. Хороший код. Ставлю вам галочку за решение... :-) - person Pretzel; 06.03.2009
comment
Хммм, похоже, что CMS придумал ответ, который я пропустил, поэтому вместо этого я награждаю его официальной галочкой ответа. Спасибо еще раз за помощь! :) - person Pretzel; 06.03.2009