格式化字典子列表的访问器可能无需每次创建新对象?

时间:2013-12-04 07:32:01

标签: c# getter

首先关闭 - 是的,我看了一下这个问题:Is object creation in getters bad practice?。 我也不是在谈论在accessors / mutators中初始化一个对象,它是关于我希望以特定方式返回的对象的特定部分。

我的问题更具体;它不一定只适用于C#,但我目前正在寻找在我的C#项目中实现的解决方案。

我有一个带有字典的类,它将日期对象映射到十进制值。在一个访问器中,我想返回字典中所有键的列表,另一个访问器返回值。 我还想要的是一个访问器,它以特定的格式给出了十进制值。它看起来像这样:

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);
        }
    }
}

这样我可以进行以下调用(这是我的目标!):

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

这种方法的问题是每次调用FormattedStrings访问器时都会创建一个新列表,即使我只需要一个格式化的字符串。我知道这不是一个好的做法,可能会引入不必要的性能问题......

我想到的替代方案是:

  1. 我可以扩展decimal类并实现自定义ToString()方法。
  2. 或者覆盖KeyValuePair<DateTime, decimal>类并在我的班级中使用索引器。
  3. 或者我创建了一个带索引参数的方法,只返回一个格式化的字符串。
  4. 或者我可以拥有一个自己的访问者列表,每次更新字典时都会在我的字典的set方法中更新。
  5. 我的问题是,有没有办法在分配值时使用访问器而不是方法,创建自定义类或对其他对象产生奇怪的副作用?

    提前谢谢。

4 个答案:

答案 0 :(得分:0)

这是VB,但你明白了......

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

答案 1 :(得分:0)

当然,这可以通过访问者来完成。您只需为已处理集合的每个所需元素创建3个单独的类。这些类应该有自己的索引器,因此您可以作为列表访问元素。不同之处在于,他们按需计算每个元素(称为lazy initialization)。所以它会像这样(你的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
        {
            // ...
        }
    }
}

现在您可以像这样使用Class

string formattedS = FormattedStrings[5];

您访问的每个元素将在您访问时计算。此解决方案还具有separating concerns的优势,因此,如果您必须为3个访问者之一实现不同的逻辑,那么只需扩展其中一个索引器即可。

您可以在此处详细了解indexeres:http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx

答案 2 :(得分:0)

您的DatesStrings属性获取者每次调用都会返回一个新列表。因此,如果呼叫者执行以下操作:

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

然后循环的每次迭代都会创建一个新列表。

我不清楚你在这里真正想要达到的目标。您正在Keys中包含字典的ValuesReadOnlyCollection属性。这为您提供了一个索引器,由于未指定Dictionary<TKey, TValue>中的键的顺序,因此它没有多大意义。

来到(最后!)你的问题,如果你想在&#34; lazy&#34;中进行格式化。方式,您可以创建一个实现只读IList<string>的自定义类,并包装您的密钥列表(IList<DateTime>)。大多数实现都是样板文件,您的索引器将执行格式化。您还可以缓存格式化的值,以便在多次访问时只格式化一次。类似的东西:

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> ...
}

答案 3 :(得分:0)

它似乎是新课程的好候选人

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

然后它可以在任何容器中使用(List<MyObject>, Dictionary<MyObject>等)。