泛型类型中的精确类型约束 - C#

Exact type constraints in generic types - C#

我想实现一个通用类型映射器,但我正在努力解决与通用类型约束相关的问题。

我在下面定义了实体映射器 class:

public class EntityMapper<TDto, TEntity>
{
    public void RegisterMapping<TDtoProperty, TEntityProperty>(
        Expression<Func<TDto, TDtoProperty>> expressionFrom,
        Expression<Func<TEntity, TEntityProperty>> expressionTo,
        Func<TDtoProperty, TEntityProperty> mapper)
    {
        // mapping
    }
}

我还有 2 个 classes 我想映射的属性:

public class Foo
{
    public long FooLongProp { get; set; }

    public decimal? FooDecimalNullProp { get; set; }

    public string FooStringProp { get; set; }
}

public class Bar
{
    public long? BarLongNullProp { get; set; }

    public decimal BarDecimalProp { get; set; }
}

正在按如下方式执行映射:

public void Map()
{
    var mapper = new EntityMapper<Foo, Bar>();

    mapper.RegisterMapping(x => x.FooLongProp, x => x.BarLongNullProp, x => x); // FooLongProp -> long, BarLongNullProp -> long?
    mapper.RegisterMapping(x => x.FooDecimalNullProp, x => x.BarLongNullProp, x => x); // FooDecimalNullProp -> decimal?, BarLongNullProp -> long?
    mapper.RegisterMapping(x => x.FooDecimalNullProp, x => x.BarDecimalProp, x => x); // FooDecimalNullProp -> decimal?, BarDecimalProp -> decimal

    mapper.RegisterMapping(x => x.FooStringProp, x => x.BarLongNullProp, x => x); // FooStringProp -> string, BarLongNullProp -> long?
}

我希望 RegisterMapping 中的第三个参数与 return 完全相同的类型,如 TEntityProperty。 不幸的是,编译器不会将下面 long -> long? 之间的映射代码报告为错误。它甚至不报告错误映射 decimal? -> decimal(这将在运行时中断)。

编译器唯一报错的地方就是我要投的那一行string -> decimal?.

所以我有以下问题:

  1. 为什么前 3 个映射没有按预期工作?它似乎 该编译器无法正确解析它们的类型。
  2. 有没有办法在泛型约束中指定完全相同的类型约束?
  3. 有没有其他可能实现我想做的事情并在编译过程中出错?

欢迎任何其他建议、评论。

mapper.RegisterMapping(x => x.FooLongProp, x => x.BarLongNullProp, x => x); // FooLongProp -> long, BarLongNullProp -> long?
  1. x => x.FooLongProp 编译器推断 (Expression<Func<Foo, long>>)
  2. x => x.BarLongNullProp 编译器推断 (Expression<Func<Bar, long?>>)
  3. x => x 编译器推断 (Func<long, long?>>)

mapper.RegisterMapping(x => x.FooDecimalNullProp, x => x.BarLongNullProp, x => x); // FooDecimalNullProp -> decimal?, BarLongNullProp -> long?
  1. x => x.FooDecimalNullProp 编译器推断 (Expression<Func<Foo, decimal?>>)

  2. x => x 编译器推断 (Func<decimal?, decimal?>>)!!为什么??因为 x=>xFunc<type of x,type of x>

  3. x => x.BarLongNullProp 编译器推断 (Expression<Func<Bar, decimal?>>) !!为什么?因为第 2 行它将 long? 向上转换为 decimal? 因此它可以匹配通用参数。


在其他情况下也会发生类似的情况。在最后一种情况下,编译器查找 Func<string, long?>。当您提供 x=>x (Func<string,string>) 时,编译器无法为您的泛型类型推断出正确的类型,因此它会失败。将鼠标光标悬停在方法调用上将显示编译器推断的通用参数。也许您应该为您的 lambda 指定一个显式类型。

mapper.RegisterMapping(x => x.FooLongProp, x => x.BarLongNullProp, x => x); 
mapper.RegisterMapping(x => x.FooDecimalNullProp, x => x.BarLongNullProp, x => (long?)x);
mapper.RegisterMapping(x => x.FooDecimalNullProp, x => x.BarDecimalProp, x => (decimal)x); 

mapper.RegisterMapping(x => x.FooStringProp, x => x.BarLongNullProp, x => (long?)(object)x ); //or any lambda which returns long?