codeflood logo

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.

Comments

Sean Hawkins

Thank you! I had the exact same problem and this is the only solution I found to it.

Alistair Deneys

Glad to be of assistance. It's quite odd that the compiler won't support this. It already checks to ensure the property has a setter when assigning a variable to it. Oh well, guess the compiler team were under pressure and it's such a fluffy feature it didn't get done.

Leave a comment

All fields are required.