StackOverflow is an awesome source of information. I guess most will use it in “I have a problem – lets ask on SO – cool, I got answer, back to work” way.. But, this site has some very, very knowledgeable people answering there, and just going through their answers will bring up a wealth of information. I’ve went through Eric Lippert’s, almost done Jon Skeets, and have a few more lined up – and a) they’re very relevant to every C# developer (even if you don’t use them in your everyday work – knowing your language of choice really well is always a good thing) b) they’re covering areas I didn’t see covered in any book, because books tend to be pretty general by their nature.
Setting property without knowing target type at compile time
Sometimes there’s a need to set a property on an object of an unknown (or uncertain) at compile time type.
There’s a few ways to do this, all different in complexity and speed. I measured the time how long it takes for each method to set a value on a property 1 mln times. The time it takes may not seem high even when using Reflection, but keep in mind this is setting just a single property with a short string. In reality it’s likely to be noticeably slower.
Say, we have this class:
public class User { public string Name { get; set; } }
and we need to set the Name property.
User user = new User();
We can use Reflection:
user.GetType().GetProperty("Name").SetValue(user, "Bob", null);
This is simplest, but by far the slowest way, taking just over 500ms.
Reflection #2:
Cache the PropertyInfo:
PropertyInfo pi = user.GetType().GetProperty("Name"); for (int i = 0; i < (1 mln); i++) pi.SetValue(user, "Bob", null);
This cuts the time down to 320ms.
Reflection #3:
Almost same as #2, but instead of caching PropertyInfo I’ve tried to cache MethodInfo – wondered if it would make any difference.
MethodInfo mi = user.GetType().GetProperty("Name").GetSetMethod(); mi.Invoke(user, new[] { "Bob" });
This is a ‘little’ faster than PropertyInfo, bringing time down to about 305ms.
Ok, now to the good stuff!
Ideally, I’d want something as close to user.Name = “Bob” as possible; this can be done using a run-time generated code.
First, a method that almost does it.
private static Action<TTargetType, TProperty> CreateSetter<TTargetType, TProperty>(Expression<Func<TTargetType, string>> propertyAccessor) { MemberExpression mex = propertyAccessor.Body as MemberExpression; string propertyName = mex.Member.Name; ParameterExpression targetObjParamExpr = Expression.Parameter(typeof(TTargetType)); ParameterExpression valueParamExpr = Expression.Parameter(typeof(TProperty)); MemberExpression propertyExpr = Expression.Property(targetObjParamExpr, propertyName); BinaryExpression assignExpr = Expression.Assign(propertyExpr, valueParamExpr); return Expression.Lambda<Action<TTargetType, TProperty>>(assignExpr, targetObjParamExpr, valueParamExpr).Compile();
}
The call looks like this:
Action<User, string> setter = CreateSetter<User, string>(x => x.Name); setter(user, "Bob");
It still requires to know the target type at compile time, so it won’t fit the requirements, but it demonstrates the use of Expression class, and how to pull a property name from an object using lambda expressions. This is great when you know the type beforehand, as I don’t have to hardcode the property name, and if it needs to be changed – it can be refactored, instead of doing search/replace.
Speed wise – this takes about 30ms, more than 10 times faster than reflection, and it’s about as fast as it will get.
OK, so now to the code that doesn’t require knowing the type upfront – pretty close to the code above (and thanks to Mark Gravell for hint):
public static Action<object, object> CreatePropertySetter(Type targetType, string propertyName) { var target = Expression.Parameter(typeof(object), "obj"); var value = Expression.Parameter(typeof(object), "value"); var property = targetType.GetProperty(propertyName); var body = Expression.Assign( Expression.Property(Expression.Convert(target, property.DeclaringType), property), Expression.Convert(value, property.PropertyType)); var lambda = Expression.Lambda<Action<object, object>>(body, target, value); return lambda.Compile(); }
The call:
Action<object, object> userNameSetter = CreatePropertySetter(user.GetType(), "Name"); userNameSetter(user, "Bob");
It returns an Action<object, object>, where first object is the object to act upon (property holder), and second object is the new property value.
This is just as fast as previous solution, something in low 30ms.
One last option, again courtesy of Mark, is to use FastMember (look it up on Nuget). This would be the easiest solution, and it’s as fast as previous 2:
ObjectAccessor accessor = ObjectAccessor.Create(user); accessor["Name"] = "Bob";
2 lines of code.. done. This also doesn’t require to create a number of delegates, each for specific property. This is great!
For comparison, when calling user.Name = “Bob”;, it takes under 10ms. So even with delegates it still takes 3 times longer, but, comparing to Reflection – this is much, much better.
Attaching Visual Studio to multiple processes
Can’t believe I just found this out… It is possible to attach VS debugger to multiple processes, so if I have a bunch w3p processes, and need to pick 1 do debug – I can just select them all (Ctrl-Alt-P, Ctrl-click).
I used to solve this with a batch file consisting of
@c:WindowsSystem32inetsrvappcmd list wp @pause
This is faster, though.
String concatenation vs StringBuilder
Got curious at which point StringBuilder actually becomes faster than plain string concatenation, so wrote a little test.
The results depend on a string length, but with a string of 30 characters the break-even point happens at about 10 concatenations. If there’s less than 10 concatenations – “abc” + “def” is faster. The longer the string – the faster StringBuilder gets ahead.
Would be interesting to plot a graph for different string lengths and number of concatenations.
EDIT:
here it is. Tested strings 20 and 100 chars long, in a 10000 times loop. X axis is number of concatenations, Y axis is milliseconds.
Pretty much what should be expected: the string concatenation times skyrocket as the number of concatenations increases, especially with long strings.
StringBuilder stays pretty flat.
Both “Location” and “Location” contain a file that deploys to the same Package location: “some file location”
I’ve tried to fix this by editing Package.Template.xml with no luck. Even though I removed the duplicate entries, the project still wouldn’t deploy or get packaged.
So finally it hit me to edit the csproj file, and bingo – it had the duplicate entries for those file. Removed those, and that was it!