C#不可变类子类

时间:2015-02-04 16:49:09

标签: c# inheritance immutability

我有一个不可变类型的类型,我想创建一个可以访问所有相同方法的子类。

但是,由于您必须实现不可变类,基类方法返回我的父类型,而不是我的子类型。是否可以创建一个可以包含返回子类的子类的不可变类?

下面是在LinqPad中运行的示例代码,用于演示问题

void Main()
{
    var immutable = new MyImmutable(new Dictionary<ImmutableKey, decimal>{
        { ImmutableKey.Key1, 1 },
        { ImmutableKey.Key2, -5 },
        { ImmutableKey.Key3, 1.25m },
    });

    var immutable2 = new MyImmutable(new Dictionary<ImmutableKey, decimal>{
        { ImmutableKey.Key1, 1 },
        { ImmutableKey.Key2, 2 },
        { ImmutableKey.Key3, 3 },
    });

    var added = immutable.Apply((a, b) => a + b, immutable2);
    added[ImmutableKey.Key1].Dump();
    added[ImmutableKey.Key2].Dump();
    added[ImmutableKey.Key3].Dump();

    var subImmutable1 = new SubImmutable(1, new Dictionary<ImmutableKey, decimal>{
        { ImmutableKey.Key1, 1 },
        { ImmutableKey.Key2, -5 },
        { ImmutableKey.Key3, 1.25m },
    });
    var subImmutable2 = new SubImmutable(1, new Dictionary<ImmutableKey, decimal>{
        { ImmutableKey.Key1, 1 },
        { ImmutableKey.Key2, 2 },
        { ImmutableKey.Key3, 3 },
    });

    var subImmutableAdded = subImmutable1.Apply((a, b) => a + b, subImmutable2);
    subImmutableAdded.GetType().Name.Dump(); //prints MyImmutable, it's not a SubImmutable
    //after adding two SubImmutables, the type is changed back to the base type

    var asSub = (SubImmutable)subImmutableAdded; // Unable to cast object of type 'MyImmutable' to type 'SubImmutable', SomeOtherValue was lost.
}

public enum ImmutableKey 
{
    Key1,
    Key2,
    Key3
}

public class MyImmutable
{
    protected static readonly IEnumerable<ImmutableKey> AllKeys = Enum.GetValues(typeof(ImmutableKey)).Cast<ImmutableKey>();

    private Dictionary<ImmutableKey, decimal> _dict { get; set; }

    public MyImmutable(Dictionary<ImmutableKey,decimal> d)
    {
        _dict = d;
    }

    public decimal this[ImmutableKey key]
    {
        get
        {
        if (_dict == null || !_dict.ContainsKey(key))
            return 0;

        return _dict[key];
        }
    }

    public MyImmutable Apply(Func<decimal, decimal, decimal> aggFunc, MyImmutable y)
    {
        var aggregated = new Dictionary<ImmutableKey, decimal>(AllKeys.Count());
        foreach (ImmutableKey bt in AllKeys)
        {
            aggregated[bt] = aggFunc(this[bt], y[bt]);
        }
        return new MyImmutable(aggregated);
    }
}

public class SubImmutable : MyImmutable
{
    public int SomeOtherValue { get; set; }
    public SubImmutable(int someValue, Dictionary<ImmutableKey,decimal> d)
        :base(d)
    {
        SomeOtherValue= someValue;
    }
}

输出:

  

2

     

-3

     

4.25

     

MyImmutable

     

InvalidCastException:无法将类型为“MyImmutable”的对象强制转换为“SubImmutable”。

有没有办法我可以拥有一个继承的不可变类型,它可以继承基类型中的所有方法,而不必重新实现它们全部?

Companion CodeReview问题:https://codereview.stackexchange.com/questions/79380/inheriting-methods-of-an-immutable-type

1 个答案:

答案 0 :(得分:2)

您可以使用虚拟方法获取新实例。

在基类中创建一个虚方法,该方法接受输入以创建基类的新实例并返回基类的新实例。然后在子类中覆盖它以生成子类所需的任何其他输入并返回子类的新实例。

public class MyImmutable
{
    // other stuff

    // add this method
    protected virtual MyImmutable GetNew(Dictionary<ImmutableKey, decimal> d)
    {
        return new MyImmutable(d);
    }

    // modify this method as shown
    public MyImmutable Apply(Func<decimal, decimal, decimal> aggFunc, MyImmutable y)
    {
        var aggregated = new Dictionary<ImmutableKey, decimal>(AllKeys.Count());
        foreach (ImmutableKey bt in AllKeys)
        {
            aggregated[bt] = aggFunc(this[bt], y[bt]);
        }
        return GetNew(aggregated);
    }
}

public class SubImmutable : MyImmutable
{
    // other stuff

    // add this method
    protected override MyImmutable GetNew(Dictionary<ImmutableKey, decimal> d)
    {
        return new SubImmutable(SomeOtherValue, d);
    }
}

这种方式任何不关心子类的转换&#39;额外的东西不需要在子类中重写。

某些转换可能仍需要覆盖。例如:

var one = new SubImmutable(1, alpha);
var two = new SubImmutable(2, alpha);
var test1 = one.Apply((a, b) => a + b, two);
var test2 = two.Apply((a, b) => a + b, one);
Console.WriteLine(test1[someKey] == test2[someKey]); // true
Console.WriteLine(test1.SomeOtherValue == test2.SomeOtherValue); // false

如果您希望test1test2拥有相同的SomeOtherValue,那么您必须将Apply方法设为虚拟,然后在子类。