Насколько медленно отражение - PullRequest
61 голосов
/ 21 апреля 2009

Я недавно создал интерфейсный слой, чтобы отличать DataAccessProvider от нашего уровня бизнес-логики. При таком подходе мы можем изменить наш выбор DataAccessProvider в любое время, изменив значения в Web / App.Config. (при необходимости можно указать больше деталей).

В любом случае, для этого мы используем отражение, чтобы выполнить наш класс DataProvider, с которым мы можем работать.

/// <summary>
/// The constructor will create a new provider with the use of reflection.
/// If the assembly could not be loaded an AssemblyNotFoundException will be thrown.
/// </summary>
public DataAccessProviderFactory()
{
    string providerName = ConfigurationManager.AppSettings["DataProvider"];
    string providerFactoryName = ConfigurationManager.AppSettings["DataProviderFactory"];
    try
    {
        activeProvider = Assembly.Load(providerName);
        activeDataProviderFactory = (IDataProviderFactory)activeProvider.CreateInstance(providerFactoryName);
    }
    catch
    {
        throw new AssemblyNotFoundException();
    }
}

Но теперь мне интересно, насколько медленное отражение?

Ответы [ 7 ]

75 голосов
/ 21 апреля 2009

В большинстве случаев: более чем достаточно быстро. Например, если вы используете это для создания объекта-оболочки DAL, время, затраченное на создание объекта с помощью отражения, будет минус по сравнению со временем, которое необходимо для подключения к сети. Поэтому оптимизация была бы пустой тратой времени.

Если вы используете отражение в замкнутом цикле, есть способы улучшить его:

  • генерики (с использованием обертки where T : new() и MakeGenericType)
  • Delegate.CreateDelegate (типизированному делегату; не работает для конструкторов)
  • Reflection.Emit - хардкор
  • Expression (как и Delegate.CreateDelegate, но более гибкий и работает для конструкторов)

Но для ваших целей, CreateInstance отлично подойдет. Придерживайтесь этого, и будьте проще.


Редактировать: пока сохраняется вопрос об относительной эффективности, и хотя остается самое важное, «измерить его», я должен пояснить некоторые из вышеперечисленных. Иногда ... это имеет значение . Измерьте в первую очередь. Однако, если вы обнаружите, что слишком медленный , вы можете посмотреть на что-то вроде FastMember , который выполняет весь код Reflection.Emit в фоновом режиме, чтобы дать вам хороший простой API; например:

var accessor = TypeAccessor.Create(type);
List<object> results = new List<object>();
foreach(var row in rows) {
    object obj = accessor.CreateNew();
    foreach(var col in cols) {
        accessor[obj, col.Name] = col.Value;
    }
    results.Add(obj);
}

что просто, но будет очень быстро. В конкретном примере, который я упоминаю об оболочке DAL - если вы делаете это много, рассмотрите что-то вроде dapper , который снова выполняет весь код Reflection.Emit в фоновом режиме, чтобы дать вам самый быстрый из возможных, но легкий используйте API:

int id = 12345;
var orders = connection.Query<Order>(
    "select top 10 * from Orders where CustomerId = @id order by Id desc",
    new { id }).ToList();
17 голосов
/ 21 апреля 2009

Это медленнее по сравнению с неотражающим кодом. Важно не то, что он медленный, а если он медленный , где он имеет значение . Например, если вы создаете экземпляры объектов, используя отражение в веб-среде, где ожидаемая конкуренция может возрасти до 10 КБ, это будет медленно.

В любом случае, хорошо не беспокоиться о производительности заранее. Если что-то идет медленно, вы всегда можете ускорить его, если вы правильно спроектировали его так, чтобы части, которые вы ожидали, могли нуждаться в оптимизации в будущем.

Вы можете проверить эту знаменитую статью, если вам нужно ускорить:

Динамический ... Но быстрый: Повесть о трех обезьянах, Волке и классах DynamicMethod и ILGenerator

5 голосов
/ 03 февраля 2016

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

С отражением

  • Создание 58 объектов путем итерации каждого из их атрибутов и сопоставления
  • Общее время: 52254 наносекунд

    while (reader.Read()) {
        string[] columns = reader.CurrentRecord;
        CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry();
        IEnumerable<PropertyInfo> rawPayFileAttributes = typeof(CdsRawPayfileEntry).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(CustomIndexAttribute)));
        foreach (var property in rawPayFileAttributes) {
            int propertyIndex = ((CustomIndexAttribute)property.GetCustomAttribute(typeof(CustomIndexAttribute))).Index;
            if (propertyIndex < columns.Length)
                property.SetValue(toReturn, columns[propertyIndex]);
            else
                break;
        }
    }
    

Без отражения

  • Создание 58 объектов путем создания нового объекта
  • Общее время: 868 наносекунд

        while (reader2.Read()) {
            string[] columns = reader2.CurrentRecord;
            CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry() {
                ColumnZero = columns[0],
                ColumnOne = columns[1],
                ColumnTwo = columns[2],
                ColumnThree = columns[3],
                ColumnFour = columns[4],
                ColumnFive = columns[5],
                ColumnSix = columns[6],
                ColumnSeven = columns[7],
                ColumnEight = columns[8],
                ColumnNine = columns[9],
                ColumnTen = columns[10],
                ColumnEleven = columns[11],
                ColumnTwelve = columns[12],
                ColumnThirteen = columns[13],
                ColumnFourteen = columns[14],
                ColumnFifteen = columns[15],
                ColumnSixteen = columns[16],
                ColumnSeventeen = columns[17]
            };
        }
    

Хотя это и не совсем справедливо, поскольку отражение также должно извлекать определенный атрибут каждого свойства 58 * 18 раз поверх создания нового объекта с помощью отражения, но оно по крайней мере обеспечивает некоторую перспективу.

5 голосов
/ 21 апреля 2009

Отражение не слишком медленное. Вызов метода с помощью отражения примерно в 3 раза медленнее, чем обычным способом. Это не проблема, если вы делаете это один раз или в некритических ситуациях. Если вы используете его 10 000 раз в критичном по времени методе, я бы подумал об изменении реализации.

3 голосов
/ 21 апреля 2009

Кроме того, что вы переходите по ссылкам, приведенным в других ответах, и гарантируете, что вы не пишете «патологически неверный» код, для меня лучший ответ на это - проверить его самостоятельно.

Только вы знаете, где находятся узкие места, сколько раз ваш код отражения будет пользовательским, будет ли код отражения замкнутым и т. Д. требования.

Однако, учитывая фрагмент кода, который вы показали здесь, тогда я предполагаю, что накладные расходы на отражение не будут большой проблемой.

Функции веб-тестирования и тестирования производительности VS.NET должны значительно облегчить измерение производительности этого кода.

Если вы не используете отражение, как будет выглядеть ваш код? Какие у него будут ограничения? Может случиться так, что вы не сможете жить с теми ограничениями, с которыми столкнетесь, если удалите код отражения. Возможно, стоит попытаться разработать этот код без размышлений, чтобы увидеть, возможно ли это или альтернатива желательна.

0 голосов
/ 25 апреля 2009

Я делал что-то похожее, пока не начал играть с IoC. Я бы использовал определение объекта Spring для указания поставщика данных - SQL, XML или Mocks!

...