You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#泛型资产估值计算器与资产ID匹配问题求解

解决方案:C#金融资产估值计算器的泛型兼容实现

针对你遇到的泛型接口赋值错误、特定参数类型无法通用调用的问题,以下是两种符合里氏替换原则的合规实现方案:

方案一:非泛型基础接口+逆变泛型接口

核心思路是通过非泛型基础接口提供统一调用入口,同时保留泛型接口的类型安全,利用逆变特性确保接口契约符合里氏替换原则。

代码实现

首先定义基础接口和泛型接口:

public interface IAssetId { }

// 非泛型基础接口,提供统一计算入口
public interface IAssetWorthCalculator
{
    decimal CalculateWorth(IAssetId assetId);
}

// 逆变泛型接口,约束资产ID类型,继承非泛型接口
public interface IAssetWorthCalculator<in TAssetId> : IAssetWorthCalculator 
    where TAssetId : IAssetId
{
    decimal CalculateWorth(TAssetId assetId);
}

实现具体的资产ID和计算器:

public class MoneyAssetId : IAssetId
{
    public decimal Amount { get; set; }
}

public class EtfAssetId : IAssetId
{
    public int ShareCount { get; set; }
    public decimal CurrentPrice { get; set; }
}

public class MoneyWorthCalculator : IAssetWorthCalculator<MoneyAssetId>
{
    // 类型安全的泛型方法,处理特定资产ID
    public decimal CalculateWorth(MoneyAssetId assetId)
    {
        return assetId.Amount * 1.0m;
    }

    // 显式实现非泛型方法,做类型检查后转发到泛型方法
    decimal IAssetWorthCalculator.CalculateWorth(IAssetId assetId)
    {
        if (assetId is not MoneyAssetId moneyAsset)
            throw new ArgumentException("不支持的资产ID类型", nameof(assetId));
        return CalculateWorth(moneyAsset);
    }
}

public class EtfWorthCalculator : IAssetWorthCalculator<EtfAssetId>
{
    public decimal CalculateWorth(EtfAssetId assetId)
    {
        return assetId.ShareCount * assetId.CurrentPrice;
    }

    decimal IAssetWorthCalculator.CalculateWorth(IAssetId assetId)
    {
        if (assetId is not EtfAssetId etfAsset)
            throw new ArgumentException("不支持的资产ID类型", nameof(assetId));
        return CalculateWorth(etfAsset);
    }
}

使用方式

遍历资产列表时,通过非泛型接口统一调用:

// 用字典映射资产类型到计算器,按需实例化
var calculators = new Dictionary<Type, IAssetWorthCalculator>
{
    { typeof(MoneyAssetId), new MoneyWorthCalculator() },
    { typeof(EtfAssetId), new EtfWorthCalculator() }
};

List<IAssetId> assets = /* 你的资产ID列表 */;
decimal totalValue = 0;

foreach (var asset in assets)
{
    if (calculators.TryGetValue(asset.GetType(), out var calculator))
    {
        totalValue += calculator.CalculateWorth(asset);
    }
    else
    {
        // 处理未支持的资产类型
        throw new NotSupportedException($"未找到{asset.GetType().Name}对应的估值计算器");
    }
}

方案二:访问者模式(双重调度)

通过访问者模式实现类型的动态匹配,避免直接类型转换,同时保持各组件职责单一,符合里氏替换原则。

代码实现

定义访问者接口和修改资产ID接口:

public interface IAssetId
{
    decimal Accept(IAssetWorthVisitor visitor);
}

public interface IAssetWorthVisitor
{
    decimal Visit(MoneyAssetId assetId);
    decimal Visit(EtfAssetId assetId);
    // 新增资产类型时,添加对应的Visit方法
}

实现资产ID和计算器(访问者):

public class MoneyAssetId : IAssetId
{
    public decimal Amount { get; set; }

    public decimal Accept(IAssetWorthVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class EtfAssetId : IAssetId
{
    public int ShareCount { get; set; }
    public decimal CurrentPrice { get; set; }

    public decimal Accept(IAssetWorthVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class MoneyWorthCalculator : IAssetWorthVisitor
{
    public decimal Visit(MoneyAssetId assetId)
    {
        return assetId.Amount * 1.0m;
    }

    // 对不支持的类型抛出异常或返回默认值,根据业务需求
    public decimal Visit(EtfAssetId assetId)
    {
        throw new NotSupportedException("该计算器不支持ETF资产估值");
    }
}

public class EtfWorthCalculator : IAssetWorthVisitor
{
    public decimal Visit(EtfAssetId assetId)
    {
        return assetId.ShareCount * assetId.CurrentPrice;
    }

    public decimal Visit(MoneyAssetId assetId)
    {
        throw new NotSupportedException("该计算器不支持货币资产估值");
    }
}

使用方式

var visitorMap = new Dictionary<Type, IAssetWorthVisitor>
{
    { typeof(MoneyAssetId), new MoneyWorthCalculator() },
    { typeof(EtfAssetId), new EtfWorthCalculator() }
};

List<IAssetId> assets = /* 你的资产ID列表 */;
decimal totalValue = 0;

foreach (var asset in assets)
{
    if (visitorMap.TryGetValue(asset.GetType(), out var visitor))
    {
        totalValue += asset.Accept(visitor);
    }
    else
    {
        throw new NotSupportedException($"未找到{asset.GetType().Name}对应的估值计算器");
    }
}

方案对比

  • 方案一更适合需要保留泛型类型安全、且资产类型可能频繁新增的场景,扩展成本低。
  • 方案二更适合资产类型相对稳定、希望避免显式类型检查的场景,代码更优雅。

内容的提问来源于stack exchange,提问作者xa19

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 02:17:34