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

能否实现字典中存储带不同类型键的强类型委托且无需强制转换?

可以实现!用泛型封装解决强类型委托存储问题

你遇到的核心问题是:Func<Person, Person, bool> 和 Func<object, object, bool> 是不同的委托类型,C# 不支持直接的隐式转换(因为委托的逆变/协变规则不允许这种方向的转换),所以直接把强类型委托塞进Dictionary<Type, Func<object, object, bool>>会编译报错。

不过我们可以通过封装泛型比较器容器的方式,既保留强类型的简洁性,又能统一存储到字典里,完全避免委托内部的强制转换。

实现步骤

  1. 定义一个非泛型接口,作为字典存储的统一类型:
public interface ITypeComparer
{
    bool Compare(object obj1, object obj2);
}
  1. 实现泛型的比较器类,内部持有强类型委托:
public class TypeComparer<T> : ITypeComparer
{
    private readonly Func<T, T, bool> _stronglyTypedComparer;

    public TypeComparer(Func<T, T, bool> comparer)
    {
        _stronglyTypedComparer = comparer;
    }

    public bool Compare(object obj1, object obj2)
    {
        // 这里的转换是安全的:我们只会根据对象Type从字典取对应比较器,所以obj1/obj2必然是T类型
        return _stronglyTypedComparer((T)obj1, (T)obj2);
    }
}
  1. 构建字典并调用:
class Program
{
    class Person { public int Id { get; set; } }
    class Dog { public string Name { get; set; } }

    static void Main(string[] args)
    {
        var funcDict = new Dictionary<Type, ITypeComparer>
        {
            { typeof(Person), new TypeComparer<Person>((p1, p2) => p1.Id == p2.Id) },
            { typeof(Dog), new TypeComparer<Dog>((d1, d2) => d1.Name == d2.Name) }
        };

        var p1 = new Person { Id = 4 };
        var p2 = new Person { Id = 4 };
        Console.WriteLine(funcDict[p1.GetType()].Compare(p1, p2)); // 输出 True

        var d1 = new Dog { Name = "旺财" };
        var d2 = new Dog { Name = "旺财" };
        Console.WriteLine(funcDict[d1.GetType()].Compare(d1, d2)); // 输出 True
    }
}

优化:用扩展方法简化字典添加

为了避免重复写typeof(T)和new TypeComparer<T>,可以加个扩展方法让代码更优雅:

public static class ComparerDictionaryExtensions
{
    public static void AddComparer<T>(this Dictionary<Type, ITypeComparer> dict, Func<T, T, bool> comparer)
    {
        dict.Add(typeof(T), new TypeComparer<T>(comparer));
    }
}

添加比较器时就可以写成:

var funcDict = new Dictionary<Type, ITypeComparer>();
funcDict.AddComparer<Person>((p1, p2) => p1.Id == p2.Id);
funcDict.AddComparer<Dog>((d1, d2) => d1.Name == d2.Name);

替代方案:用dynamic(不推荐)

如果你追求极简代码,也可以用dynamic作为字典值类型,但这种方式没有编译时类型检查,运行时传错类型会报错,且性能不如泛型封装:

var funcDict = new Dictionary<Type, dynamic>
{
    { typeof(Person), (Func<Person, Person, bool>)((p1, p2) => p1.Id == p2.Id) },
    { typeof(Dog), (Func<Dog, Dog, bool>)((d1, d2) => d1.Name == d2.Name) }
};

// 调用时直接传强类型参数
Console.WriteLine(funcDict[p1.GetType()](p1, p2));

为什么原来的写法不行?

C# 委托的逆变/协变规则:Func<in T1, in T2, out TResult> 中,输入参数是逆变的(允许从object转到Person,但反过来不行),输出是协变的。所以Func<Person, Person, bool>不能隐式转换为Func<object, object, bool>,这就是你最初的代码编译失败的原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 14:12:56