如何将运行时类型匹配逻辑迁移至继承类且避免类膨胀?
兄弟我太懂你这种从F#转C#的纠结了!之前天天跟DU打交道,习惯了那种“定义好所有类型,随便加行为”的爽感,突然跳进C#的继承多态世界,还要处理前人留下的一堆is/as类型判断,想转多态又怕一不小心类就炸得满天飞,确实头大。
先顺着你说的DU和继承的区别捋:DU是“锁死类型,开放行为”,继承是“锁死行为,开放类型”,所以迁移的核心就是把原来分散在外部的类型判断逻辑,收拢到各自的子类里,同时用一些技巧控制类的复杂度,避免膨胀。给你几个实用的路子:
第一步:把类型判断里的行为“归位”到子类
比如之前你可能有个这样的方法,全是类型判断:public void HandlePayment(Payment payment) { if (payment is CreditCardPayment cc) { /* 信用卡支付逻辑 */ } else if (payment is PayPalPayment pp) { /* PayPal支付逻辑 */ } // 更多类型判断... }这时候直接把
Handle逻辑拆成基类的抽象方法,让每个子类自己实现:public abstract class Payment { public abstract void Handle(); } public class CreditCardPayment : Payment { public override void Handle() { // 专属信用卡的支付逻辑 } }调用的时候直接
payment.Handle(),再也不用写类型判断了。这是最基础的多态迁移,先把分散的逻辑收拢到各自的“责任主体”里。用接口拆分行为,别给基类塞一堆冗余方法
要是你有N种不同的行为(比如Handle、Validate、Refund),全塞基类里让每个子类都实现一遍,肯定会让类变臃肿。这时候把行为拆成独立的接口,子类按需实现:public interface IHandleable { void Handle(); } public interface IRefundable { bool Refund(decimal amount); }比如
CreditCardPayment既实现IHandleable又实现IRefundable,而有些一次性支付类型可能只需要IHandleable。这样每个子类只需要对自己负责的行为编码,不用被迫实现一堆无关的方法,自然就不会膨胀。重复逻辑抽出来,别让每个子类都写一遍
多个子类有相同的逻辑?别复制粘贴!比如很多支付类型的Validate逻辑里都要检查“金额是否大于0”,直接把这部分抽到基类的虚方法里:public abstract class Payment { public virtual bool Validate() { // 通用校验:金额合法性 return Amount > 0; } } public class CreditCardPayment : Payment { public override bool Validate() { if (!base.Validate()) return false; // 信用卡专属校验:卡号、有效期 return IsCardNumberValid() && IsExpiryValid(); } }也可以搞个静态辅助类,把通用逻辑放进去,子类直接调用,比如
PaymentHelpers.ValidateBaseAmount(this.Amount),这样能大幅减少子类的代码冗余。别随便加子类:用“状态”代替“类型差异”
很多时候你觉得要加个子类,其实只是某个属性或者状态不同而已。比如“人民币信用卡支付”和“美元信用卡支付”,没必要搞RmbCreditCardPayment和UsdCreditCardPayment两个子类,直接给CreditCardPayment加个Currency枚举属性就行。
记住:只有当两个东西的核心行为逻辑不同时,才需要拆分成子类;如果只是配置、状态不一样,用属性或者枚举就能搞定,别为了这点差异新增类。
总的来说,从DU转C#搞多态,核心就是适应OOP“行为固定时扩展类型”的思路,别硬套DU的模式。先把类型判断的逻辑收拢到子类,再用接口拆分、逻辑复用、状态代替子类这几招控制复杂度,既能摆脱讨厌的运行时类型匹配,又不会搞出一堆冗余类。
备注:内容来源于stack exchange,提问作者Lyndon Gingerich

