Более удобный способ использования делегата функции над делегатом действия - PullRequest
0 голосов
/ 14 марта 2012

Я хочу перебрать коллекцию потокобезопасным способом.Я считаю целесообразным иметь один метод с именем

/// <summary>
/// This visits all items and performs an action on them in a thread manner
/// </summary>
/// <param name="visitAction">The action to perform on the item</param>
public void VisitAllItems(Action<Item> visitAction) {
    lock (listLock) {
        foreach (Item item in this.ItemList) {
            visitAction.Invoke(item);
        }
    }
}

Примером его использования может быть

/// <summary>
/// Saves each item in the list to the database
/// </summary>
protected static void SaveListToDatabase() {
    this.VisitAllItems(item => {
        bool itemSavedSuccessfully = item.SaveToDB();
        if(!itemSavedSuccessfully) {
            //log error message
        }
    });
}

Другим примером его использования будет

/// <summary>
/// Get the number of special items in the list.
/// </summary>
protected int GetNumberOfUnsynchronisedItems() {
    int numberOfSpecialItems = 0;
    this.VisitAllItems((item) => {
        numberOfSpecialItems += item.IsItemSpecial() ? 1 : 0;
    });
    return numberOfSpecialItems;
}

Но я уверен, что есть лучший способ написания метода VisitAllItems для использования Func<> вместо делегата Action<> для возврата этого значения.Я пробовал несколько вещей, но в конечном итоге с ошибками компиляции.

Кто-нибудь может увидеть более аккуратный способ реализации этого метода?

Спасибо, Alasdair

Ответы [ 2 ]

2 голосов
/ 14 марта 2012

Все зависит от использования, поэтому сложно сказать, что именно будет полезно для вас. Но один из способов - вернуть один результат для каждого элемента в коллекции, а затем использовать LINQ для объединения результатов.

public IEnumerable<TResult> VisitAllItems<TResult>(
    Func<Item, Result> visitfunction)
{
    var result = new List<TResult>();
    lock (listLock)
    {
        foreach (Item item in ItemList)
            result.Add(visitfunction(item));
    }
    return result;
}

Обычно я бы использовал yield return вместо того, чтобы вручную создавать List<T>, но я думаю, что так будет лучше здесь из-за lock.

Использование будет выглядеть так:

protected int GetNumberOfUnsynchronisedItems()
{
    return VisitAllItems(item => item.IsItemSpecial())
                  .Count(isSpecial => isSpecial);
}
2 голосов
/ 14 марта 2012

Вы можете следовать модели Aggregate, но она не будет ужасно приятной в использовании:

public TAccumulate VisitAllItems<TAccumulate>(TAccumulate seed,
    Func<Item, TAccumulate, TAccumulate> visitor) {
    TAccumulate current = seed;
    lock (listLock) {
        foreach (Item item in this.ItemList) {
            current = visitor(current, item);
        }
    } 
    return current;
}

...

protected int GetNumberOfUnsynchronisedItems() {
    return VisitAllItems(0, 
        (count, item) => count + item.IsItemSpecial() ? 1 : 0);
}

Сколько различных функций агрегации делаютвам на самом деле нужно?Например, если в большинстве случаев времени, которое вы только что подсчитали, вы можете захотеть:

public void VisitAndCount<TAccumulate>(Func<Item, bool> visitor) {
    int count = 0;
    lock (listLock) {
        foreach (Item item in this.ItemList) {
            if (visitor(item)) {
                count++;
            }
        }
    }
    return count;
}

тогда:

protected int GetNumberOfUnsynchronisedItems() {
    return VisitAndCount(item => item.IsItemSpecial());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...