0

first off - yes, I had a look at this question: Is object creation in getters bad practice?. I am also not talking about initializing an object in the accessors / mutators, it is about a specific part of the object I want to be returned in a specific way.

My question is more specific; It does not necessarily only apply to C#, however I am currently looking for a solution to implement in my C# project.

I have a class with a dictionary that maps date objects to a decimal value. In one accessor, I want to return a list of all the keys of the dictionary, another accessors returns the values. What I also want to have is an accessor that gives me the decimal values in a specific format. It would look something like this:

class Class
{
    // Some other properties...
    // ....

    private Dictionary<DateTime, decimal> dict;

    public Class(Dictionary<DateTime, decimal> dict)
    {
        this.dict = dict;
    }

    private string FormatTheWayIWant(decimal dt)
    {
        // Format decimal value.
        string s = String.Format("{0:F}", dt);
        return s;
    }

    public ReadOnlyCollection<DateTime> DateTimes
    {
        get { return new ReadOnlyCollection<DateTime>(this.dict.Keys.ToList()); }
    }

    public ReadOnlyCollection<decimal> Values
    {
        get { return new ReadOnlyCollection<decimal>(this.dict.Values.ToList()); }
    }

    public ReadOnlyCollection<string> FormattedStrings
    {
        get
        {
            // Format each decimal value they way I want.
            List<string> list = new List<string>();
            foreach (decimal dt in dict.Keys)
            {
                list.Add(FormatTheWayIWant(dt));
            }
            return new ReadOnlyCollection<string>(list);
        }
    }
}

This way I can make the following calls (which is my goal!):

DateTime dateTime = DateTimes[0];
decimal s = Values[0];
string formattedS = FormattedStrings[0];

The problem with this approach is that I create a new list everytime I invoke the FormattedStrings accessor, even if I only need one of the formatted strings. I know this is not good practice and can introduce unnecessary performance issues...

The alternatives I thought of are:

  1. I could extend the decimal class and implement a custom ToString()-method.
  2. Or overwrite the KeyValuePair<DateTime, decimal> class and use an indexer in my class.
  3. Or I create a method with a parameter for the index and return just the one formatted string.
  4. Or I could have an own list for the accessor, which gets updated in the set-method for my dictionary everytime I update the dictionary.

The question I have is, is there a way to make this work with an accessor instead of a method, creating custom classes or having strange side effects on other objects when assigning a value?

Thank you in advance.

Community
  • 1
  • 1
Yeehaw
  • 105
  • 2
  • 11
  • I edited the question and added decimals instead of strings or the values of the dictionary. This should make it clearer. – Yeehaw Dec 04 '13 at 22:31

4 Answers4

0

This is VB, but you get the idea...

Public Class Something

  Public Property Items As Dictionary(Of DateTime, String)

  Public Readonly Property FormattedItem(ByVal index As Int32) As String
    ' add error checking/handling as appropriate
    Return Me.Items.Keys(index).ToString("custom format")  '  or whatever your formatting function looks like.
  End Property
End Class
Sam Axe
  • 33,313
  • 9
  • 55
  • 89
  • Thank you for this, I was actually more looking for a generic solution to get a sub-object of an object in an accessor. This is a good solution for formatting the string, I'm sure I can make use of that some time. – Yeehaw Dec 04 '13 at 21:49
0

Ofcourse this can be done with an accessor. You just have to create 3 separate classes for each desired element of your processed collection. Those classes should have their own indexers, so you would be able to access the elements as a list. The difference would be, that they compute each element on demand (wchich is called lazy initialization). So it would go like this (example for your FormattedStrings):

class Class
{
    // ...

    MyFormattedStrings FormattedStrings
    {
        get {return new MyFormattedStringsIndexer<string>(this.dict.Values.ToList());}
    }
}

class MyFormattedStringsIndexer<T>
{
    private IList<T> list;  // we take only reference, so there is no overhead

    public MyFormattedStringsCollection (IList<T> list)
    {
        this.list = list;
    }

    // the indexer:
    public T this[int i]
    {
        get
        {
            // this is where the lazy stuff happens:
            // compute the desired element end return it
        }
        set
        {
            // ...
        }
    }
}

Now you can use your Class like this:

string formattedS = FormattedStrings[5];

and each element you access will be computed as you access it. This solution also has the advantage of separating concerns, so should you ever had to implement different logic for one of your 3 accessors it would be just a matter of extending one of the indexers.

You can read more about indexeres here: http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx

Maciej Sz
  • 11,151
  • 7
  • 40
  • 56
  • Thank you for your answer. I actually wanted to keep the dictionary because I want to access decimal values for specific date. I'm not sure if your solution would be suitable in terms of maintainability since it not straight-forward that dates and decimal values belong together. – Yeehaw Dec 04 '13 at 22:48
  • Sorry, I kinda messed that up. Of course I still have the dictionary in the class `Class`... I really like your solution, it produces no overhead and keeps everything clean. Thank you! (I would give you an upvote if I could) – Yeehaw Dec 04 '13 at 23:40
0

Your Dates and Strings property getters are returning a new list on each call. Therefore if a caller does the following:

Class myClass = ...
for(i=0; i<myClass.Strings.Count; i++)
{
    var s = myClass.Strings[i];
    ...
}

then each iteration of the loop will create a new list.

I'm not clear on what you're really trying to achieve here. You are wrapping the dictionary's Keys and Values properties in ReadOnlyCollections. This gives you an indexer, which doesn't have much meaning as the order of the Keys in a Dictionary<TKey, TValue> is unspecified.

Coming (at last!) to your question, if you want to do the formatting in a "lazy" manner, you could create a custom class that implements a readonly IList<string>, and wraps your list of keys (IList<DateTime>). Most of the implementation is boilerplate, and your indexer will do the formatting. You could also cache the formatted values so that you only format once if accessed multiple times. Something like:

public class MyFormattingCollection : IList<string>
{
    private IList<decimal> _values;
    private IList<string> _formattedValues;

    public MyFormattingCollection(IList<DateTime> values)
    {
        _values = values;
        _formattedValues = new string[_values.Count];
    }

    public string this[int index]
    {
        get
        {
            var result = _formattedValues[index];
            if (result == null)
            {
                result = FormatTheWayIWant(_values[index]);
                _formattedValues[index] = result;
            }
            return result;
       }
       set
       {
           // Throw: it's a readonly collection
       }
    }

    // Boilerplate implementation of readonly IList<string> ...
}
Joe
  • 122,218
  • 32
  • 205
  • 338
  • To your first remark - using a SortedDictionary would be better in my case I guess, since I would like the dictionary entries to be sorted by date and time. With my accessors I tried to return only those objects that I really need. This means that when I'm looking for a specific date in my dictionary, I just access the DateTimes property b/c I don't need to know about the according value just yet. – Yeehaw Dec 04 '13 at 21:56
  • A question about your solution - You initialize the object at a specific index lazily. But once it is initialized, if refers to the same object all the time. What would you do if the underlying dictionary changes and I need to update the formatted string representation of that dictionary entry? – Yeehaw Dec 04 '13 at 21:59
  • @Yeehaw, if you want to do things the "lazy" way, and this includes the lists you create for the "DateTimes" and "Values" properties, then you'd need to reinitialize the lazily-generated fields each time you update the dictionary. Just to be clear, I wouldn't actually recommend this solution. IMHO it would be better just to let the caller call a formatting method when accessing the raw values – Joe Dec 05 '13 at 08:26
0

It looks like a good candidate for a new class

public class MyObject
{
    public DateTime Key {get;set;}
    public String Name {get;set;}
    public String FormattedString {get;}
}

And then it can be used in any container (List<MyObject>, Dictionary<MyObject>, etc).

Artur A
  • 7,115
  • 57
  • 60
  • If I wanted a collection of all formatted strings, I would have to access the FormattedString for every single item in my `List` object. Doesn't that add unnecessary overhead? – Yeehaw Dec 04 '13 at 22:29
  • There will not be any difference in overhead. If you need collection of FormattedString items then you will iterate it anyway (whether preparing it for indexer). It will be more clear to use OO principle. Instead of writing [int index = 1;/*then apply found index*/ FormattedStrings[index] you can write myObject.FormattedString]. Just ask your colleague what is more clear, and if it requires much more time to understand and to use your code than it could be done with OO ---> spending of his life and company money. – Artur A Dec 05 '13 at 08:03