Как медленно отражается
Недавно я создал интерфейсный слой, чтобы отличить 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();
}
}
Но теперь мне интересно, как медленное отражение?
Ответы
Ответ 1
В большинстве случаев: более чем достаточно быстро. Например, если вы используете это для создания объекта-оболочки DAL, время, затрачиваемое на создание объекта посредством отражения, будет minuscule по сравнению с временем, которое требуется для подключения к сети. Поэтому оптимизация этого будет пустой тратой времени.
Если вы используете отражение в узком цикле, есть уловки, чтобы улучшить его:
- generics (используя обертку
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();
Ответ 2
Его медленнее по сравнению с неотражающим кодом. Важно не то, если он медленный, но если его медленный , где он считает. Например, если вы создаете объекты, используя отражение в веб-среде, где ожидаемая согласованность может вырасти до 10K, это будет медленным.
В любом случае, это хорошо, чтобы не беспокоиться о производительности заранее. Если все оказывается слишком медленным, вы всегда можете ускорить их, если вы правильно разработали, чтобы части, которые вы ожидали, нуждались в оптимизации в будущем, локализованы.
Вы можете проверить эту знаменитую статью, если вам нужно ускорить:
Динамический... Но быстрый: рассказ о трех обезьянах, волках и классах DynamicMethod и ILGenerator
Ответ 3
Вот некоторые ссылки, которые могут помочь:
Ответ 4
Отражение не так медленно. Вызов метода отражением примерно в 3 раза медленнее обычного. Это не проблема, если вы делаете это только один раз или в некритических ситуациях. Если вы используете его 10'000 раз в критическом по времени методе, я бы подумал об изменении реализации.
Ответ 5
Помимо ссылок, приведенных в других ответах, и обеспечения того, что вы не пишете "патологически плохой" код, для меня лучшим ответом на это является проверка его самостоятельно.
Только вы знаете, где у вас бутылочные шеи, сколько раз ваш код отражения будет пользователем, будет ли код отражения в жестких петлях и т.д. Вы знаете свое деловое дело, сколько пользователей будет получать доступ к вашему сайту, требования.
Однако, учитывая фрагмент кода, который вы показали здесь, я предполагаю, что накладные расходы на отражение не будут серьезной проблемой.
Возможности веб-тестирования и тестирования производительности VS.NET должны сделать измерение производительности этого кода довольно простым.
Если вы не используете отражение, как будет выглядеть ваш код? Какие ограничения у него будут? Возможно, вы не можете жить с ограничениями, с которыми вы сталкиваетесь, если вы удалите код отражения. Возможно, стоит попробовать создать этот код без отражения, чтобы увидеть, возможно ли это, или же это альтернатива желательна.
Ответ 6
Я думал, что сделаю быстрый тест, чтобы продемонстрировать, насколько медленное отражение сравнивается без него.
С отражением
- Выполнение 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 раз поверх создания нового объекта посредством отражения, но он по крайней мере обеспечивает некоторую перспективу.
Ответ 7
Я делал нечто похожее до тех пор, пока не начал играть с IoC. Я бы использовал определение объекта Spring для указания поставщика данных - SQL, XML или Mocks!