C# 不可变类子类

标签 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: Unable to cast object of type 'MyImmutable' to type 'SubImmutable'.

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

配套代码审查问题:https://codereview.stackexchange.com/questions/79380/inheriting-methods-of-an-immutable-type

最佳答案

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

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

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

这样,任何不关心子类额外内容的转换都不需要在子类中被覆盖。

一些转换可能仍然需要被覆盖。例如:

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 方法虚拟,然后在子类中覆盖它。

关于C# 不可变类子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28326884/

相关文章:

java - 为什么我的子类不继承其父类(super class)的私有(private)实例变量?

ios - Swift 函数中的动态修饰符

javascript - 仅使用与先前嵌套键不匹配的新数据更新不可变列表?

c# - Miniprofiler 在缺少 CreatedOn 列时中断

Ruby 继承 - super 初始化得到错误数量的参数

c# - 找到缺失的月份

data-structures - F# 不可变可变大小窗口数据结构

python - python 中用户定义的不可变变量,不带子类化

c# - 在 DbContext 的子类中使用反射调用 DbSet<Type>.Add() 方法

c# - 从 Azure 文件共享下载并删除文件