I made a dictionary-like container that supports key-wise arithmetic operations. I want it to "act" like a value type in the sense that it can be added, subtracted, and multiplied by other instances of the same container.

I'm making a set of stats containers for a game. The aforementioned arithmetic dictionary is to be the base class for all of the different stats containers (i.e. stats, stat multipliers, equipment stat multipliers, level up bonuses, etc.) using a common enum for their keys and a variety of data types for their values (i.e. integers, doubles, and number-like custom classes).

public class ArithmeticDictionary<TKey,TValue> : 
    IEnumerable<KeyValuePair<TKey,TValue>>,
    System.Numerics.IAdditionOperators<
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>>,
    System.Numerics.ISubtractionOperators<
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>>,
    System.Numerics.IMultiplyOperators<
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>>,
    System.Numerics.IComparisonOperators<
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>,
        bool>,
    System.Numerics.IMinMaxValue<
        ArithmeticDictionary<TKey,TValue>>,
    System.Numerics.IAdditiveIdentity<
        ArithmeticDictionary<TKey,TValue>,
        ArithmeticDictionary<TKey,TValue>>
where TKey : notnull
where TValue : 
    System.Numerics.IAdditionOperators<TValue,TValue,TValue>,
    System.Numerics.ISubtractionOperators<TValue,TValue,TValue>,
    System.Numerics.IMultiplyOperators<TValue,TValue,TValue>,
    System.Numerics.IComparisonOperators<TValue,TValue,bool>,
    System.Numerics.IMinMaxValue<TValue>,
    System.Numerics.IAdditiveIdentity<TValue,TValue>
{}

The functionality is all there, but every time I subclass the generic base class, I get a whole bunch of the same warning.

Here, Stat is an enum.

public class Stats : 
    ArithmeticDictionary<Stat, uint>,
    IEnumerable<KeyValuePair<Stat,uint>>,
    System.Numerics.IAdditionOperators<Stats,Stats,Stats>,
    System.Numerics.ISubtractionOperators<Stats,Stats,Stats>,
    System.Numerics.IMultiplyOperators<Stats,Stats,Stats>,
    System.Numerics.IComparisonOperators<Stats,Stats,bool>,
    System.Numerics.IMinMaxValue<Stats>,
    System.Numerics.IAdditiveIdentity<Stats,Stats>
{}

warning CA2260: The 'ArithmeticDictionary<TKey, TValue>' requires the 'TKey' type parameter to be filled with the derived type 'Stats'

From the warning, it seems to be due to ArithmeticDictionary implementing a number of interfaces that have a TSelf type parameter, but child classes are no longer of the same type.

My code still works, but 5 subclasses with 6 warnings each is a bit much.

1

There are 1 best solutions below

5
Menno van Lavieren On BEST ANSWER

Add a TSelf type parameter to IArithmeticDictionary. Inline with the answer to this question. Adding operator support to interfaces (Preview Feature in .NET 6)

I put some example code below that compiles, but ommits simmilar cases for brevity.

public interface IArithmeticDictionary<TSelf, TKey, TValue> :
    IEnumerable<KeyValuePair<TKey, TValue>>,
    System.Numerics.IAdditionOperators<TSelf,
        TSelf,
        TSelf>
where TSelf : IArithmeticDictionary<TSelf, TKey, TValue>
where TKey : notnull
where TValue :
    System.Numerics.IAdditionOperators<TValue, TValue, TValue>
{
   // Optional
    public static TSelf operator +(IArithmeticDictionary<TSelf, TKey, TValue> left, TSelf right)
    {
        throw new NotImplementedException();
    }
    // Optional
    static TSelf operator checked +(IArithmeticDictionary<TSelf, TKey, TValue> left, TSelf right)
    {
        throw new NotImplementedException();
    }
}

public class Stats :
    IArithmeticDictionary<Stats, Stat, uint>
{
    public IEnumerator<KeyValuePair<Stat, uint>> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public static Stats operator +(Stats left, Stats right)
    {
        throw new NotImplementedException();
    }
}

public enum Stat
{
    A,
    B
}