Может Делегировать. DynamicInvoke избежаться в этом общем коде?

Если Ваши элементы являются типами значения, то можно просто сделать:

List<YourType> newList = new List<YourType>(oldList);

Однако, если они - ссылочные типы и Вы хотите глубокую копию (предполагающий, что Ваши элементы правильно реализуют ICloneable), Вы могли сделать что-то вроде этого:

List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);

oldList.ForEach((item) =>
    {
        newList.Add((ICloneable)item.Clone());
    });

, Очевидно, замените ICloneable в вышеупомянутых дженериках и броске с тем, что Ваш тип элемента - то, который реализует ICloneable.

, Если Ваш тип элемента не поддерживает ICloneable, но действительно имеет конструктора копии, Вы могли бы сделать это вместо этого:

List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);

oldList.ForEach((item)=>
    {
        newList.Add(new YourType(item));
    });

Лично, я избежал бы ICloneable из-за потребности гарантировать глубокую копию всех участников. Вместо этого я предложил бы конструктора копии или метод фабрики как YourType.CopyFrom(YourType itemToCopy), который возвращает новый экземпляр YourType.

Любая из этих опций могла быть обернута методом (расширение или иначе).

11
задан Community 23 May 2017 в 10:31
поделиться

2 ответа

Я сильно подозреваю, что упаковка вызовов будет намного эффективнее, чем использование DynamicInvoke . Тогда ваш код будет:

internal sealed class TypeDispatchProcessor
{
    private readonly Dictionary<Type, Action<object>> _actionByType 
        = new Dictionary<Type, Action<object>>();

    public void RegisterProcedure<T>(Action<T> action)
    {
        _actionByType[typeof(T)] = item => action((T) item);
    }

    public void ProcessItem(object item)
    {
        Action<object> action;
        if (_actionByType.TryGetValue(item.GetType(), out action))
        {
            action(item);
        }
    }
}

Его стоит протестировать, но я думаю, вы найдете это намного более эффективным. DynamicInvoke должен проверять все аргументы с помощью отражения и т. Д. Вместо простого преобразования в обернутом делегате.

23
ответ дан 3 December 2019 в 02:41
поделиться

Итак, я провел некоторые измерения.

var delegates = new List<Delegate>();
var actions = new List<Action<object>>();

const int dataCount = 100;
const int loopCount = 10000;

for (int i = 0; i < dataCount; i++)
{
    Action<int> a = d => { };
    delegates.Add(a);
    actions.Add(o => a((int)o));
}

var sw = Stopwatch.StartNew();
for (int i = 0; i < loopCount; i++)
{
    foreach (var action in actions)
        action(i);
}
Console.Out.WriteLine("{0:#,##0} Action<object> calls in {1:#,##0.###} ms",
    loopCount * dataCount, sw.Elapsed.TotalMilliseconds);

sw = Stopwatch.StartNew();
for (int i = 0; i < loopCount; i++)
{
    foreach (var del in delegates)
        del.DynamicInvoke(i);
}
Console.Out.WriteLine("{0:#,##0} DynamicInvoke calls in {1:#,##0.###} ms",
    loopCount * dataCount, sw.Elapsed.TotalMilliseconds);

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

Результаты весьма убедительны!

1,000,000 Action calls in 47.172 ms
1,000,000 Delegate.DynamicInvoke calls in 12,035.943 ms

1,000,000 Action calls in 44.686 ms
1,000,000 Delegate.DynamicInvoke calls in 12,318.846 ms

Итак, в этом случае заменяем вызов на DynamicInvoke для дополнительного косвенного вызова и приведения были примерно 270 раз быстрее . Все в день работы.

7
ответ дан 3 December 2019 в 02:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: