У меня есть общий метод расширения, который принимает параметр выражения. Параметр используется для сборки, компиляции и кэширования дерева выражений. Эта часть работает хорошо (выражение компилируется только один раз).Выражение <Func<T>> параметр вызывает вызов кода для использования Expression.Lambda
Но когда я просматриваю приложение, я вижу время, проведенное в Expression.Lambda и Expression.Property в , вызывающем код. Является ли это ожидаемым или я сделал что-то неправильно в определении/компиляции моих выражений?
Вот упрощенная версия кода участвуют:
namespace Example
{
public class Caller
{
public void SetPropertyX(MyTypedDataRow dataRow, string value)
{
// Inside this method, the profiler shows time spent in Expression.Lambda and Expression.Property
// even when the same property is reused and the compiled expression stored in the dictionary of
// DataRowExpressionHelper is used.
dataRow.Set(x => x.PropertyX, value);
}
}
public static class DataRowExtensions
{
public static void Set<TRow, TValue>(this TRow row, Expression<Func<TRow, TValue>> property, TValue value) where TRow : DataRow
{
DataRowExpressionHelper<TRow>.Set(row, property, value);
}
}
internal static class DataRowExpressionHelper<TRow> where TRow : DataRow
{
private static Dictionary<string, object> _propertySetters = new Dictionary<string, object>();
internal static void Set<TValue>(TRow row, Expression<Func<TRow, TValue>> property, TValue value)
{
var propertyInfo = (PropertyInfo) ((MemberExpression) property.Body).Member;
var propertyName = propertyInfo.Name;
Action<TRow, TValue> propertySetter;
object untypedSetter;
if (_propertySetters != null && _propertySetters.TryGetValue(propertyName, out untypedSetter))
{
propertySetter = (Action<TRow, TValue>)untypedSetter;
}
else
{
var targetRow = Expression.Parameter(typeof(TRow), "targetRow");
var newValue = Expression.Parameter(typeof(TValue), "newValue");
propertySetter = Expression.Lambda<Action<TRow, TValue>>(
Expression.Block(/* Logic here */),
// Input parameters
targetRow,
newValue
).Compile();
var updatedPropertySetters = new Dictionary<string, object>(_propertySetters);
updatedPropertySetters[propertyName] = propertySetter;
_propertySetters = updatedPropertySetters;
}
propertySetter.Invoke(row, value);
}
}
Код будет использовать ветвь компиляции при первом вызове имени свойства, поэтому по меньшей мере один вызов будет отображаться в профилировщике с момента его первого вызова. –
@ScottChamberlain, я думаю, вы, возможно, ударили ноготь по голове там. Я, конечно же, ожидал, что для компиляции будут вызовы Expression, но я ожидал их внутри метода DataRowExpressionHelper.Set, где происходит .Compile(). Но когда я смотрю сейчас, я не вижу никаких вызовов выражения. * Внутри DataRowExpressionHelper.Set, поэтому кажется, что вместо этого появляется вызов в коде вызова. –
См. [Рекомендации по оптимизации передаваемых выражений в качестве параметров метода] (http://stackoverflow.com/questions/41847754/suggestions-for-optimizing-passing-expressions-as-method-parameters) –