views:

62

answers:

3

I have two functions whose underlying logic is the same but in one case it sets one property value on a class and in another case it sets a different one. How can I rewrite the following two functions to abstract away as much of the algorithm as possible so that I can make changes in logic in a single place?

SetBillingAddress

private void SetBillingAddress(OrderAddress newBillingAddress)
{
    BasketHelper basketHelper = new BasketHelper(SiteConstants.BasketName);
    OrderAddress oldBillingAddress = basketHelper.Basket.Addresses[basketHelper.BillingAddressID];

    bool NewBillingAddressIsNotOldBillingAddress = ((oldBillingAddress == null) || (newBillingAddress.OrderAddressId != oldBillingAddress.OrderAddressId));
    bool BillingAddressHasBeenPreviouslySet = (oldBillingAddress != null);
    bool BillingAddressIsNotSameAsShippingAddress = (basketHelper.ShippingAddressID != basketHelper.BillingAddressID);
    bool NewBillingAddressIsNotShippingAddress = (newBillingAddress.OrderAddressId != basketHelper.ShippingAddressID);

    if (NewBillingAddressIsNotOldBillingAddress && BillingAddressHasBeenPreviouslySet && BillingAddressIsNotSameAsShippingAddress)
    {
        basketHelper.Basket.Addresses.Remove(oldBillingAddress);
    }

    if (NewBillingAddressIsNotOldBillingAddress && NewBillingAddressIsNotShippingAddress)
    {
        basketHelper.Basket.Addresses.Add(newBillingAddress);
    }

    basketHelper.BillingAddressID = newBillingAddress.OrderAddressId;
    basketHelper.Basket.Save();
}

And here is the second one:

SetShippingAddress

private void SetShippingAddress(OrderAddress newShippingAddress)
{
    BasketHelper basketHelper = new BasketHelper(SiteConstants.BasketName);
    OrderAddress oldShippingAddress = basketHelper.Basket.Addresses[basketHelper.ShippingAddressID];

    bool NewShippingAddressIsNotOldShippingAddress = ((oldShippingAddress == null) || (newShippingAddress.OrderAddressId != oldShippingAddress.OrderAddressId));
    bool ShippingAddressHasBeenPreviouslySet = (oldShippingAddress != null);
    bool ShippingAddressIsNotSameAsBillingAddress = (basketHelper.ShippingAddressID != basketHelper.BillingAddressID);
    bool NewShippingAddressIsNotBillingAddress = (newShippingAddress.OrderAddressId != basketHelper.BillingAddressID);

    if (NewShippingAddressIsNotOldShippingAddress && ShippingAddressHasBeenPreviouslySet && ShippingAddressIsNotSameAsBillingAddress)
    {
        basketHelper.Basket.Addresses.Remove(oldShippingAddress);
    }

    if (NewShippingAddressIsNotOldShippingAddress && NewShippingAddressIsNotBillingAddress)
    {
        basketHelper.Basket.Addresses.Add(newShippingAddress);
    }

    basketHelper.ShippingAddressID = newShippingAddress.OrderAddressId;
    basketHelper.Basket.Save();
}

My initial thought was that if I could pass a class's property by refernce then I could rewrite the previous functions into something like

private void SetPurchaseOrderAddress(OrderAddress newAddress, ref String CurrentChangingAddressIDProperty)

and then call this function and pass in either basketHelper.BillingAddressID or basketHelper.ShippingAddressID as CurrentChangingAddressIDProperty but since I can't pass C# properties by reference I am not sure what to do with this code to be able to reuse the logic in both places.

Thanks for any insight you can give me.

+2  A: 

You can pass lambda expressions:

private void SetPurchaseOrderAddress(OrderAddress newAddress, Func<string> addressIDGetter, Action<string> addressIDSetter) 

Call it like this:

SetPurchaseOrderAddress(newAddress, () => basketHelper.BillingAddressID, a => basketHelper.BillingAddressID = a);
SLaks
+2  A: 

Send in a Func and an Action, one to get the property out, and another to set it again.

private void SetPurchaseOrderAddress(OrderAddress newAddress, 
          Func<BasketHelper, int> getAddId, Action<BasketHelper, int> setAddId) {}

Which would give something like:

SetPurchaseOrderAddress(address, 
    bh => bh.Basket.Addresses[bh.BillingAddressID],
    (bh,s) => bh.BillingAddressID = s);
Benjol
Note that you can't pass properties by reference because under the hood they are actually two methods (getter and setter), I know because I tried this afternoon! (cf. http://stackoverflow.com/questions/529782/c-property-and-ref-parameter-why-no-sugar)
Benjol
A: 

You can use delegates to create a method that takes some simple behavior (such as setting a value of the property) as an argument. In C# 3.0, you can easily create a delegate using lambda expressions and in C# 2.0 you can do similar thing using anonymous delegates.

In your example, you would write a method that takes Action<BasketHelper> delegate - the delegate allows you to specify some operation that should be done with the BasketHelper after you initialize it and before you save it:

private void SetBasketProperty(Action<BasketHelper> action)  { 
  BasketHelper basketHelper = new BasketHelper(SiteConstants.BasketName); 
  // Code that is repeated for all similar methods

  // Invoke the specific action    
  action(basketHelper);
  basketHelper.Basket.Save(); 
} 

To call this method in C# 3.0 you can write something like this:

SetBasketProperty(basketHelper => {
    basketHelper.ShippingAddressID = newShippingAddress.OrderAddressId;
  });

The lambda expression simply provides the code that you want to run before calling Save.

Tomas Petricek