Using Properties in Out Parameters in C#
Recently I was working on some DTO code that required setting a current value and an old value for a variety of properties on the DTO. The purpose of the DTO was to transfer changes to user details from inside Sitecore over XML to another system. But if a property hadn't changed then it shouldn't be populated into the DTO. And although the following is straight forward, it just felt like the code was a little bloated.
var dto = new DTO();
dto.ID = Sitecore.Context.User.Name;
var profile = Sitecore.Context.User.Profile;
if (!string.IsNullOrEmpty(profile.Name) && profile.Name !=
newValues["name"])
{
dto.OldName = profile.Name;
dto.NewName = newValues["name"];
}
if (!string.IsNullOrEmpty(profile.GetCustomProperty("address")) &&
profile.GetCustomProperty("address") != newValues["address"])
{
dto.OldAddress = profile.GetCustomProperty("address");
dto.NewAddress = newValues["address"];
}
if (!string.IsNullOrEmpty(profile.GetCustomProperty("state")) &&
profile.GetCustomProperty("state") != newValues["state"])
{
dto.OldState = profile.GetCustomProperty("state");
dto.NewState = newValues["state"];
}
That's only a fragment of the code. There were 15 to 20 properties that has to be set in that fashion. The problem with the above code is that if I wanted to change the logic around whether to include a set of properties or not I would have to go through each block of code and update the logic. Actually, that null checking at the start of each block wasn't originally in my code, I had to add it to each of the code blocks. Some refactoring was in order!
So I started out by creating a method to handle the error handling and change checking and populating the values of each property.
static void PopulateProfileProperty(
Sitecore.Security.UserProfile profile, string propertyName,
Dictionary<string, string> newValues, out string oldProp,
out string newProp)
{
oldProp = null;
newProp = null;
if (!string.IsNullOrEmpty(profile.GetCustomProperty(propertyName))
&& profile.GetCustomProperty(propertyName) !=
newValues[propertyName])
{
oldProp = profile.GetCustomProperty(propertyName);
newProp = newValues[propertyName];
}
}
Note the out
parameters so the values are passed back out of the method. Now I can simplify the original code by calling this method.
PopulateProfileProperty(profile, "address", newValues,
out dto.OldAddress, out dto.NewAddress);
PopulateProfileProperty(profile, "state", newValues,
out dto.OldState, out dto.NewState);
Hit the compile button and...
error CS0206: A property or indexer may not be passed as an out or ref parameter
Get several of those. That's right, in C# object properties can't be passed as out parameters. Apparently it's something to do with the compiler not checking to ensure the property has a setter.
The normal way to get around that is to simply use local variables to store output values in and assign into the object.
string oldAddress, newAddress;
PopulateProfileProperty(profile, "address", newValues, out oldAddress,
out newAddress);
dto.OldAddress = oldAddress;
dto.NewAddress = newAddress;
string oldState, newState;
PopulateProfileProperty(profile, "state", newValues, out oldState,
out newState);
dto.OldState = oldState;
dto.NewState = newState;
But that almost looks worse than the original code!
So I got to thinking about how I could pass a property to the out parameter. Hello delegates. A delegate is like a method pointer. It defines the signature of a method that can be passed like a parameter.
So let's define a delegate which can be passed into the PopulateProfileProperty method above to provide the abstraction we need.
delegate void PropertySetter(string newValue, string oldValue);
Now we can refactor the PopulateProfileProperty
method to use the delegate rather than the out
parameters.
static void PopulateProfileProperty(
Sitecore.Security.UserProfile profile,
string propertyName, Dictionary<string, string> newValues,
PropertySetter setter)
{
if (!string.IsNullOrEmpty(profile.GetCustomProperty(propertyName)) &&
profile.GetCustomProperty(propertyName) != newValues[propertyName])
{
setter(newValues[propertyName], profile.GetCustomProperty(
propertyName));
}
}
The delegate allows us to pass in the actual code which does the setting. Different .net versions allow you to do this in different ways, but I like using lambda expressions to create anonymous delegates.
PopulateProfileProperty(profile, "address", newValues,
(newValue, oldValue) => { dto.NewAddress = newValue;
dto.OldAddress = oldValue; });
PopulateProfileProperty(profile, "state", newValues,
(newValue, oldValue) => { dto.NewState = newValue;
dto.OldState = oldValue; });
And now we can compile. And although this is a little esoteric, getting around the limitations of one area of the language with another language feature was fun :) . And the final code is much cleaner.
Thank you! I had the exact same problem and this is the only solution I found to it.