patterncsharpMinor
Inheriting methods of an immutable type
Viewed 0 times
methodsinheritingtypeimmutable
Problem
In my project, I have a type
I have another class called a
Below are the classes we have in place. (Currently they are structs, but we are considering changing them to classes since they have grown in storage and complexity recently)
```
public struct BalanceByBucket : IEquatable
{
private static readonly IEnumerable _AllBucketTypes = Enum.GetValues(typeof(BucketType)).Cast();
private static readonly BalanceByBucket zeroBalance = new BalanceByBucket(0);
public static BalanceByBucket Zero { get { return zeroBalance; } }
public static IEnumerable AllBucketTypes { get { return _AllBucketTypes; } }
private ImmutableDictionary _valueDictionary;
private decimal _total;
public decimal Total { get { return _total; } }
public decimal this[BucketType bucket]
{
get
{
if (_valueDictionary == null || !_valueDictionary.ContainsKey(bucket))
return 0;
return _valueDictionary[bucket];
}
}
///
/// This is the base constructor. all other constructors should point to this one.
///
///
public BalanceByBucket(Dictionary values)
{
var dict = new Dictionary();
foreach (var item in values)
dict.Add(item.Key, item.Value);
_valueDictionary = dict.ToImmutableDictionary();
_total = _valueDictionary.Sum(x => x.Value);
}
private BalanceByBucket(decimal initValue)
: this(AllBucket
BalanceByBucket that is an immutable type and has a bunch of methods.I have another class called a
FundBalance which is basically just a BalanceByBucket that has an extra int FundID property applied to it. I want this FundBalance to also have access to all the same methods as BalanceByBucket, but remain immutable and return FundBalances instead of BalanceByBuckets. Is there a better way to implement this so that FundBalance can do everything a BalanceByBucket can do without having to reimplement every method?Below are the classes we have in place. (Currently they are structs, but we are considering changing them to classes since they have grown in storage and complexity recently)
BalanceByBucket: ```
public struct BalanceByBucket : IEquatable
{
private static readonly IEnumerable _AllBucketTypes = Enum.GetValues(typeof(BucketType)).Cast();
private static readonly BalanceByBucket zeroBalance = new BalanceByBucket(0);
public static BalanceByBucket Zero { get { return zeroBalance; } }
public static IEnumerable AllBucketTypes { get { return _AllBucketTypes; } }
private ImmutableDictionary _valueDictionary;
private decimal _total;
public decimal Total { get { return _total; } }
public decimal this[BucketType bucket]
{
get
{
if (_valueDictionary == null || !_valueDictionary.ContainsKey(bucket))
return 0;
return _valueDictionary[bucket];
}
}
///
/// This is the base constructor. all other constructors should point to this one.
///
///
public BalanceByBucket(Dictionary values)
{
var dict = new Dictionary();
foreach (var item in values)
dict.Add(item.Key, item.Value);
_valueDictionary = dict.ToImmutableDictionary();
_total = _valueDictionary.Sum(x => x.Value);
}
private BalanceByBucket(decimal initValue)
: this(AllBucket
Solution
Firstly I would like to describe some generic recommendations (and performance improvements, as I assume it is quite a critical data structure for you):
-
Since you are building an immutable type, it would be better to mark fields as
-
-
There is no need to create a copy of dictionary before calling
-
-
-
-
You have a bug in
Now back to your original question - "
As a result, I would try to have a single structure/class that provides required functionality. If you want to be able to reference the
-
Since you are building an immutable type, it would be better to mark fields as
readonly.private readonly ImmutableDictionary _valueDictionary;
private readonly decimal _total;-
_AllBucketTypes will enumerate the enum every time you start iterating on, it, so it is better to capture the result in array.private static readonly BucketType[] _AllBucketTypes = Enum.GetValues(typeof(BucketType)).Cast().ToArray();-
There is no need to create a copy of dictionary before calling
ToImmutableDictionary() in BalanceByBucket(Dictionary), and you can use IDictionary as a parameter type. Also you can optimize the performance of the BalanceByBucket by adding another constructor which accepts ImmutableDictionary (optimizations shown below).public BalanceByBucket(IDictionary values)
: this(values.ToImmutableDictionary())
{
}
private BalanceByBucket(ImmutableDictionary values)
{
_valueDictionary = values;
_total = values.Sum(x => x.Value);
}-
Select() call is redundant in the BalanceByBucket(IEnumerable>), and you can directly convert to ImmutableDictionary to save on memory allocations and transformationsprivate BalanceByBucket(IEnumerable> bucketsWithValue)
: this(bucketsWithValue
.GroupBy(x => x.Key)
.ToImmutableDictionary(x => x.Key, x => x.Sum(y => y.Value)))
{
}-
Apply(Func) and other Apply methods can use new constructor to avoid redundant memory allocations.public BalanceByBucket Apply(Func aggFunc)
{
if (aggFunc == null)
throw new ArgumentNullException("aggFunc");
if (_valueDictionary == null)
return Zero.Apply(aggFunc);
return new BalanceByBucket(_valueDictionary.ToImmutableDictionary(entry => entry.Key, entry => aggFunc(entry.Value)));
}-
CreateFrom(IEnumerable) can be slightly simplifiedpublic static BalanceByBucket CreateFrom(IEnumerable balances)
{
return new BalanceByBucket(
balances.SelectMany(x => x.Buckets._valueDictionary ?? Enumerable.Empty>()));
}-
You have a bug in
operator /(BalanceByBucket, BalanceByBucket) implementation, you're throwing DivideByZeroException based on x instead of y values. Also, you don't actually need to throw this exception explicitly, it will be thrown anyway by Apply((a, b) => a / b, x, y) expression.Now back to your original question - "
FundBalance can do everything a BalanceByBucket can do without having to reimplement every method":- Operator overloads cannot be reused, you would have to reimplement them anyway.
- Both structures have references to each other which is not great.
FundBalanceappears to add a very little value on top ofBalanceByBucket.
As a result, I would try to have a single structure/class that provides required functionality. If you want to be able to reference the
BalanceByBucket functionality without explicit reference to FundBalance, consider switching from struct to class and define an interface for BalanceByBucket functionality.Code Snippets
private readonly ImmutableDictionary<BucketType, decimal> _valueDictionary;
private readonly decimal _total;private static readonly BucketType[] _AllBucketTypes = Enum.GetValues(typeof(BucketType)).Cast<BucketType>().ToArray();public BalanceByBucket(IDictionary<BucketType, decimal> values)
: this(values.ToImmutableDictionary())
{
}
private BalanceByBucket(ImmutableDictionary<BucketType, decimal> values)
{
_valueDictionary = values;
_total = values.Sum(x => x.Value);
}private BalanceByBucket(IEnumerable<KeyValuePair<BucketType, decimal>> bucketsWithValue)
: this(bucketsWithValue
.GroupBy(x => x.Key)
.ToImmutableDictionary(x => x.Key, x => x.Sum(y => y.Value)))
{
}public BalanceByBucket Apply(Func<decimal, decimal> aggFunc)
{
if (aggFunc == null)
throw new ArgumentNullException("aggFunc");
if (_valueDictionary == null)
return Zero.Apply(aggFunc);
return new BalanceByBucket(_valueDictionary.ToImmutableDictionary(entry => entry.Key, entry => aggFunc(entry.Value)));
}Context
StackExchange Code Review Q#79380, answer score: 2
Revisions (0)
No revisions yet.