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

Blazor服务端应用:如何简化接口继承检查与批量调用逻辑?

问题与解答:Blazor动态组件批量调用泛型接口方法

初始场景

我开发了一个Blazor服务端应用,在页面中使用DynamicComponent实现组件的运行时创建与添加。最初定义了如下接口:

public interface IGetData
{
    Task GetDataAsync(string condition);
}

public interface IGetDataA : IGetData
{
    Task GetDataAsync(CriteriaA criteria);
}

public interface IGetDataB : IGetData
{
    Task GetDataAsync(CriteriaB criteria);
}

在组件创建后,页面中的RefreshAsync方法分两次筛选、转换并批量调用GetDataAsync:

public async Task RefreshAsync()
{
    var itemsA = items.Where(x => x.Ref.Instance is IGetDataA)
        .Select(x => x.Ref.Instance)
        .Cast<IGetDataA>();

    await Parallel.ForEachAsync(itemsA, async (item, token) =>
    {
        await item.GetDataAsync(criteriaA);
    });

    var itemsB = items.Where(x => x.Ref.Instance is IGetDataB)
        .Select(x => x.Ref.Instance)
        .Cast<IGetDataB>();

    await Parallel.ForEachAsync(itemsB, async (item, token) =>
    {
        await item.GetDataAsync(criteriaB);
    });
}

我想知道能否将上述逻辑合并为一次转换与循环?

更新后的场景

我对接口进行了简化,改为泛型接口:

public interface IGetData<T> 
{
    Task GetDataAsync(string condition);
    Task GetDataAsync(T data);
}

并调整了代码:

bool IsGetData(object o) => o.GetType().GetInterfaces().Any(x =>
    x.IsGenericType &&
    x.GetGenericTypeDefinition() == typeof(IGetData<>));

var list = items.Where(x => IsGetData(x.Ref.Instance))
    .Select(x => x.Ref.Instance);

await Parallel.ForEachAsync(list, async (item, token) =>
{
    if (item is ItemA)
        await (item as ItemA).GetDataAsync(criteriaA);
    else if (item is ItemB)
        await (item as ItemB).GetDataAsync(criteriaB);
});

现在代码多了一个辅助方法,还需要用is和as判断类型,请问能否进一步简化这段代码?


简化方案

方案1:模式匹配简化类型判断

直接利用C#模式匹配完成类型转换与筛选,去掉冗余的辅助方法和as操作:

await Parallel.ForEachAsync(items, async (wrapper, token) =>
{
    var instance = wrapper.Ref.Instance;
    switch (instance)
    {
        case IGetData<CriteriaA> dataA:
            await dataA.GetDataAsync(criteriaA);
            break;
        case IGetData<CriteriaB> dataB:
            await dataB.GetDataAsync(criteriaB);
            break;
        // 新增IGetData<T>类型时,直接添加对应分支即可
    }
});
  • 无需额外筛选逻辑,模式匹配自动识别实现了IGetData<T>的实例
  • 一次遍历所有items,合并了原有的两次筛选与循环
  • 直接在case中完成类型转换,避免冗余的类型判断代码

方案2:标记接口+模式匹配(更直观的类型筛选)

给泛型接口添加一个非泛型标记接口,简化类型筛选逻辑:

// 新增标记接口
public interface IGetDataMarker { }
public interface IGetData<T> : IGetDataMarker
{
    Task GetDataAsync(string condition);
    Task GetDataAsync(T data);
}

调用代码简化为:

await Parallel.ForEachAsync(items.Where(x => x.Ref.Instance is IGetDataMarker), async (wrapper, token) =>
{
    var instance = wrapper.Ref.Instance;
    switch (instance)
    {
        case IGetData<CriteriaA> dataA:
            await dataA.GetDataAsync(criteriaA);
            break;
        case IGetData<CriteriaB> dataB:
            await dataB.GetDataAsync(criteriaB);
            break;
    }
});
  • 标记接口让类型筛选更直观,无需反射判断泛型接口
  • 保持了模式匹配的简洁性,代码可读性更高

方案3:字典映射+反射(高扩展性场景)

如果后续会频繁新增IGetData<T>的实现,可以用字典维护类型与参数的映射,避免修改调用逻辑:

// 提前初始化类型-参数映射,可放在类静态构造或初始化方法中
private static readonly Dictionary<Type, object> _criteriaMap = new()
{
    { typeof(CriteriaA), criteriaA },
    { typeof(CriteriaB), criteriaB }
};

// 调用逻辑
await Parallel.ForEachAsync(items, async (wrapper, token) =>
{
    var instance = wrapper.Ref.Instance;
    var getDataType = instance.GetType().GetInterfaces()
        .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IGetData<>));
    
    if (getDataType != null)
    {
        var genericArg = getDataType.GetGenericArguments()[0];
        if (_criteriaMap.TryGetValue(genericArg, out var criteria))
        {
            var method = getDataType.GetMethod(nameof(IGetData<object>.GetDataAsync), new[] { genericArg });
            if (method != null)
            {
                await (Task)method.Invoke(instance, new[] { criteria });
            }
        }
    }
});
  • 新增IGetData<T>类型时,只需在字典中添加对应参数映射,无需修改调用逻辑
  • 适合组件类型较多、扩展性要求高的场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 21:05:10