1

Let's say I have a class called Customer with some properties:

public class Customer
{
    public string CustAddr = GetCustAddr(); //Long procedure returning address

    public double CustAcct = GetCustAcct(); //Long procedure returning account

    public string CustInvoice = GetCustInvoice(); //Long procedure returning invoice
}

This class is returned via a function:

public Customer GetData(string query)
{
    Customer cust = new Customer();
    //set the values for each of the properties
    return cust;
}

Right now it is returning the whole class and I can use it like this:

lblDisplay = GetData("QUERY").CustAddr.ToString();

However, suppose each property takes a lot of time to compute. If I only want the CustAddr value, it still computes and has CustAcct and CustInvoice available for me.

How do I alter my function to only return the property I'm looking for, aside from breaking up my class into individual procedures to call? For example, I could just:

lblDisplay = GetCustAddr().ToString();

but that's not what I'm looking for. I think it's better to have all my data in an organized structure instead of a bunch of different procedures.

gnarlybracket
  • 1,691
  • 4
  • 18
  • 37

3 Answers3

5

This is a great candidate for lazy initialization. Here I present an example using properties (you have fields).

public class Customer
{
    public Lazy<string> CustAddr { get; private set; }
    public Lazy<double> CustAcct { get; private set; }
    public Lazy<string> CustInvoice { get; private set; }

    public Customer()
    {
        CustAddr = new Lazy<string>(GetCustAddr);
        CustAcct = new Lazy<double>(GetCustAcct);
        CustInvoice = new Lazy<string>(GetCustInvoice);
    }
}

However, I would be remiss if I didn't point out that you should not use floating-point types (float, double) to store monetary values. Consider instead using decimal.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • You point out using `double` instead of `decimal` but you don't bring up the fact that he is exposing public fields not properties? – Scott Chamberlain Sep 29 '14 at 21:26
  • 3
    That said, it's not clear why OP doesn't store the object in a variable and access each property one at a time instead of always creating it via `GetData`. – Tim Schmelter Sep 29 '14 at 21:27
  • 1
    @ScottChamberlain I am editing to use properties as well. But if I had to pick one of those two to fix, I would indeed pick usage of floating-point types. Properties begets easier maintenance; fixed-point for monetary data begets *not making bad calculations*. – cdhowie Sep 29 '14 at 21:27
  • 1
    One might want to hide the `Lazy` from the consumer and just expose the wrapped object via a property. Otherwise, you're leaking implementation details. – Nicholas Carey Sep 29 '14 at 21:42
  • @NicholasCarey I thought about doing that as well, but decided against it at least for this particular example. I have heard arguments for and against doing that, so I will let OP choose which approach makes the most sense for this situation. – cdhowie Sep 29 '14 at 21:53
2

Why use an exotic solution when there's a vanilla one?

Implementing the calculation on the get of the properties.

NB: What you have are public fields, not properties

Very simple solution (nothing exotic!)

See this fiddle

e.g.

public class Customer
{
    private double ? _custAcct = null;
    public double CustAcct
    {
        get
        {
            if (!_custAcct.HasValue)
            {
                _custAcct = GetCustAcct();
            }

            return _custAcct.Value;
        }
    }

    private double GetCustAcct()
    {
        // do something that takes a long time
        return 1234.45;
    }
}
BanksySan
  • 27,362
  • 33
  • 117
  • 216
1

What you're talking about is lazy loading or lazy initialization. You write your properties something like this:

public class MyLazyWidget
{
  . . .
  public BigExpensiveObject MyLazyProperty
  {
    get
    {
      if ( BigExpensiveObjectBackingStore == null )
      {
        BigExpensiveObjectBackingStore = ExpensiveOperation() ;
      }
        return BigExpensiveObjectBackingStore ;
    }
  }
  private static BigExpensiveObjectBackingStore = null ;
  . . .
}

And if your app is multi-threaded, you'll need to worry about race conditions, so you need to synchronize access to the static backing store:

public class MyLazyWidget
{
  . . .
  public BigExpensiveObject MyLazyProperty
  {
    get
    {
      lock( MyLazyPropertyLatch )
      {
        if ( BigExpensiveObjectBackingStore == null )
        {
          BigExpensiveObjectBackingStore = ExpensiveOperation() ;
        }
      }
      return BigExpensiveObjectBackingStore ;
    }
  }
  private static readonly object MyLazyPropertyLatch = new object() ;
  private static BigExpensiveObjectBackingStore = null ;
  . . .
}
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135