Reputation: 150253
I have a method similar to this one:
static string GetVariableName<T>(Expression<Func<T>> expression)
{
var body = expression.Body as MemberExpression;
return body.Member.Name;
}
That give me the variables names. Everyone who mentions Reflection say It's bad for performance, So I want to cache the result so the reflection can occur only one single time for each var. Example:
GetVariableName(() => Model.Field1) // Does Reflection.
GetVariableName(() => Model.Field2) // Does Reflection.
GetVariableName(() => Model.Field1) // Uses Cache.
GetVariableName(() => Model.Field2) // Uses Cache.
I'm using this Util to log parameters And I want start using it to produce JQuery
selectors in Asp.net Mvc3
application
$('#'+ @(GetVariableName(()=> Model.FieldName))).Val();
Any ideas?
Upvotes: 1
Views: 1156
Reputation: 1038710
Everyone who mentions Reflection say It's bad for performance
Sure, but in this case you already have the MemberInfo from the lambda expression. The compiler has already built the expression tree. You don't need to fetch it using reflection which is what is slow. What would have been expensive is the following:
static string GetVariableName(string expression)
{
// use reflection to find the property given the string and once you have the property
// get its name
...
}
That's how all the strongly typed helpers in ASP.NET MVC work. You don't need to cache anything if you use the strongly typed lambda expression version.
Upvotes: 2
Reputation: 52107
You should be able to do something like this...
class Foo {
public Foo() {
m_Field1Name = new Lazy<string>(() => GetVariableName(() => Field1));
m_Field2Name = new Lazy<string>(() => GetVariableName(() => Field2));
}
public int Field1 { get; set; }
public int Field2 { get; set; }
public string Field1Name {
get {
return m_Field1Name.Value;
}
}
readonly Lazy<string> m_Field1Name;
public string Field2Name {
get {
return m_Field2Name.Value;
}
}
readonly Lazy<string> m_Field2Name;
public static string GetVariableName<T>(Expression<Func<T>> expression) {
var body = expression.Body as MemberExpression;
return body.Member.Name;
}
}
Benchmarking the cached names versus non-cached shows significant difference...
class Program {
static void Main(string[] args) {
var foo = new Foo();
const int count = 1000000;
var sw = new Stopwatch();
sw.Restart();
for (int i = 0; i < count; ++i) {
string name1 = foo.Field1Name;
string name2 = foo.Field2Name;
}
sw.Stop();
Console.Write("Cached:\t\t");
Console.WriteLine(sw.Elapsed);
sw.Restart();
for (int i = 0; i < count; ++i) {
string name1 = Foo.GetVariableName(() => foo.Field1);
string name2 = Foo.GetVariableName(() => foo.Field2);
}
sw.Stop();
Console.Write("Non-cached:\t");
Console.WriteLine(sw.Elapsed);
}
}
This prints:
Cached: 00:00:00.0176370
Non-cached: 00:00:12.9247333
Upvotes: 1
Reputation: 17498
Have you considered using attributes? You could reflect over the model once and cache those results instead.
[AttributeUsage(AttributeTargets.Property, AllowMultiple= false)]
class JQueryFieldNameAttribute : Attribute {
public string Name { get; private set; }
public JQueryFieldNameAttribute(string name)
{
Name = name;
}
}
class Model {
[JQueryFieldName("#clientid")]
public string Foo { get; set; }
}
void Main()
{
var type = typeof(Model);
var attributes = type.GetProperties()
.SelectMany (t => t.GetCustomAttributes(typeof(JQueryFieldNameAttribute), true));
var cache = new Dictionary<int, IEnumerable<JQueryFieldNameAttribute>>();
// Cache results for this type only
cache.Add(type.GetHashCode(), attributes);
foreach (JQueryFieldNameAttribute a in attributes)
{
Console.WriteLine (a.Name);
}
}
Upvotes: 0