Как заменить элемент в коллекции - PullRequest
0 голосов
/ 24 августа 2018

То, что я хочу сделать, может показаться очень простым - я хочу найти элемент в ICollection<T>, который удовлетворяет одному предикату, и заменить его другим. В C ++ я бы написал так:

for(auto &element : collection) {

    if(predicate(elem)) {
        element = newElement;
    }
}

Возьмите элемент по ссылке и переназначьте его. Однако делать

foreach(ref var element in collection)

в C # не удается скомпилировать, и я не уверен, что он будет делать то, что я хочу, если он скомпилируется. Как получить доступ к физической ссылке в коллекции, чтобы изменить ее?

Моя подпись метода, если она помогает:

public static void ReplaceReference<T>(
    ICollection<T> collection, 
    T newReference, 
    Func<T, bool> predicate)

EDIT:

Поскольку это кажется неясным, я не могу просто взять ICollection<T> и заменить его на что-то другое. Я получаю ICollection - это все, что я знаю, и я не могу это изменить. Неважно, как бы я хотел, чтобы это был IList или IEasilyReplacable, я не могу на это повлиять.

Ответы [ 3 ]

0 голосов
/ 24 августа 2018

Поэтому я ударился головой о стену и придумал действительно простое решение для конкретной проблемы замены: найти, удалить, а затем добавить.

var existing = collection.FirstOrDefault(predicate);

if (existing != null)
{
    collection.Remove(existing);    
    collection.Add(newReference);
}

Тем не менее, я рассматриваю это скорее как обходной путь к моей проблеме foreach, и поэтому разместил этот вопрос как продолжение: Извлечь элемент из коллекции по ссылке в foreach

EDIT:

Для комментария Дэниела А. Уайта:

Обработка только первого была тем, что я намеревался сделать, но это может быть легко изменено, чтобы заменить все:

var existing = collection.Where(predicate);

foreach(var element in existing)
{
    collection.Remove(element);
}

for(int i = 0; i < existing.Count); ++i)
{
    collection.Add(newReference);
}

Что касается заказа - ICollection не обязательно заказывается. Таким образом, для исправления этого можно было бы создать новый метод с менее общей подписью

static void ReplaceReference<T>(
    IList<T> list, 
    T newReference, 
    Func<T, bool> predicate)

, который будет использовать индексатор для замены значений

for(int i = 0; i < list.Count; ++i)
{
    if(predicate(list[i]))
    {
        list[i] = newReference;
        // break here if replace-one variant.
    }
}

И теперь в основном методе мы проверяем, является ли наша коллекция IList, поэтому упорядоченным, и передаем его в упорядоченную версию:

if(collection is IList<T> list)
{
    ReplaceReference(list, newReference, predicate);
    return;
}

=============================================== ============================

Sidenote: конечно, есть также подход dumbo:

var newCollection = new List<T>();

foreach(var element in collection)
{
    newList.Add(predicate(element) ? newReference : element);
}

collection.Clear();

foreach(var newElement in newCollection)
{
    collection.Add(newElement);
}

но это крайне неэффективно.

0 голосов
/ 24 августа 2018

Насколько я понимаю, вы хотите заменить конкретный элемент в коллекции на основе данного предиката, я попробовал приведенный ниже код, и он отлично работает для меня.

Я создал список строк из 4 элементов и попросил мой универсальный метод найти строку со значением «Имя 1», если оно истинно, следует изменить его на значение «Имя 5».

Я протестировал его с помощью консольного приложения, поэтому вы можете протестировать его, создав forloop, который показывает значения списка с помощью Console.WriteLine ();

    public void Main(string[] args)
    {
        List<string> list = new List<string>();
        list.Add("Name 1");
        list.Add("Name 2");
        list.Add("Name 3");
        list.Add("Name 4");

        Func<string, bool> logicFunc = (listItemValue) => listItemValue == "Name 1";
        ReplaceReference(list, "Name 5", logicFunc);
    }

    public static void ReplaceReference<T>(ICollection<T> collection, T newReference, Func<T, bool> predicate)
    {
        var typeName = typeof(T).Name;
        var newCollection = collection.ToList();
        for (int i = 0; i < newCollection.Count; i++)
        {
            if (predicate(newCollection[i]))
            {
                newCollection[i] = newReference;
            }
        }
    }
0 голосов
/ 24 августа 2018
  • ICollection<T> не будет лучшим для этого сценария.IList<T> позволяет назначать с помощью индексатора.
  • Другой вариант - создание новой коллекции во время итерации.
  • Вы также можете написать какую-то оболочку, которая является реальной ссылкой вколлекция и содержит значение:
ICollection<Wrapper<T>> collection = ...;

foreach(var wrapper in collection)
{
    wrapper.Value = newValue;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...