泛型类型约束和方差
本文关键字:方差 约束 泛型类型 | 更新日期: 2023-09-27 18:33:59
我正在构建一个简单的类型映射器,类似于AutoMapper,但具有更动态的行为。调用方可以决定在从实体框架模型映射时筛选RecordStatus == RecordStatus.Deleted
记录。
抽象映射器:
public interface IMapper<in TIn, out TOut>
{
TOut Map(TIn input);
}
public interface IRecordStatusFilterable
{
string RecordStatus { get; }
}
public abstract class RecordStatusFilterableMapperBase<TIn, TOut> : IMapper<TIn, TOut>
{
private readonly bool _filterDeletedRecords;
protected RecordStatusFilterableMapperBase(bool filterDeletedRecords)
{
_filterDeletedRecords = filterDeletedRecords;
}
protected bool FilterDeletedRecords
{
get { return _filterDeletedRecords; }
}
public abstract TOut Map(TIn input);
}
public class MultiLookupValuesMapper : RecordStatusFilterableMapperBase<IEnumerable<Lookup>, string>
{
private static readonly Func<Lookup, bool> _predicate =
filterable => filterable.RecordStatus == RecordStatus.Active;
protected MultiLookupValuesMapper(bool filterDeletedRecords) : base(filterDeletedRecords)
{
}
public override string Map(IEnumerable<Lookup> input)
{
var inputList = input as IList<Lookup> ?? input.ToList();
if (!inputList.Any())
{
return string.Empty;
}
if (FilterDeletedRecords)
{
inputList = (IList<Lookup>)inputList.Where(_predicate);
}
return string.Join(", ", inputList.Select(l => l.Value));
}
}
混凝土测绘仪:
public class FooMapper<TRecordStatusFilterable> : RecordStatusFilterableMapperBase<Foo, FooViewModel>
where TRecordStatusFilterable : class, IRecordStatusFilterable
{
private readonly IMapper<IEnumerable<TRecordStatusFilterable>, string> _multiLookupValueMapper;
public FooMapper(IMapper<IEnumerable<TRecordStatusFilterable>, string> multiLookupValueMapper,
bool filterDeletedRecords) : base(filterDeletedRecords)
{
_multiLookupValueMapper = multiLookupValueMapper;
}
public override FooViewModel Map(Foo input)
{
return new FooViewModel
{
// Error here
BarLookupValues = _multiLookupValueMapper.Map(input.Lookups)
};
}
}
实体框架模型:
public class Foo
{
public ICollection<Lookup> Lookups { get; set; }
}
public class Lookup : IRecordStatusFilterable
{
public string Value { get; set; }
public string RecordStatus { get; set; }
}
视图模型:
public class FooViewModel
{
// ICollection<Lookup> => string
public string BarLookupValues { get; set; }
}
我收到编译错误:
参数 1:无法从"System.Collections.Generic.IEnumerable
"转换为"System.Collections.Generic.IEnumerable "
但是我的 Lookup
类确实满足泛型类型参数约束,因为它实现了 IRecordStatusFilterable
.谁能对此有所了解?
实际上,
很多代码与实际问题无关。这是一个更简单的版本,希望能更好地说明它:
class MyList<T>
where T : class, IConvertible
{
private List<T> list = new List<T>();
public void Add(string s)
{
list.Add(s); // error
}
}
是的,T
是受约束的,string
符合约束,但这并不意味着您可以string
添加到任意 T 列表中。那不会打字安全。
如果我定义
class Bar : IConvertible { /* left out IConvertible impl */ }
并做了一个var bars = new MyList<Bar>()
很明显,向柱线中添加字符串对于泛型类中的该代码来说是一个问题。
刚刚有一个更复杂的版本,我不是100%确定你到底想表达什么。也许类FooMapper
根本不应该是通用的,应该只采用IMapper<IEnumerable<Lookup>, string>
的实例。